diff --git a/CMakeLists.txt b/CMakeLists.txt index 09931847..963aae37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,20 @@ EXECUTE_PROCESS(COMMAND ) IF (NOT "${FTE_REVISON}" STREQUAL "") - SET(FTE_REVISON SVNREVISION=${FTE_REVISON}) + MESSAGE(STATUS "FTE SVN Revision ${FTE_REVISON}") + IF(FTE_REVISON MATCHES "M") + MESSAGE(STATUS "--- PRIVATE CHANGES DETECTED ---") + SET(FTE_REVISON SVNREVISION=${FTE_REVISON}) + ELSE() + MESSAGE(STATUS "No local changes") + EXECUTE_PROCESS(COMMAND + svn info --show-item last-changed-date --no-newline + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + OUTPUT_VARIABLE FTE_DATE + ) + + SET(FTE_REVISON SVNREVISION="${FTE_REVISON}" SVNDATE="${FTE_DATE}") + ENDIF() ENDIF() #plugins need visibility hidden in order to avoid conflicts with function names that match the engine. @@ -981,7 +994,7 @@ ADD_LIBRARY(irc MODULE plugins/plugin.c plugins/irc/ircclient.c ) -SET_TARGET_PROPERTIES(irc PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES}") +SET_TARGET_PROPERTIES(irc PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_REVISON};${FTE_LIB_DEFINES}") SET_TARGET_PROPERTIES(irc PROPERTIES PREFIX "fteplug_") SET_TARGET_PROPERTIES(irc PROPERTIES LINK_FLAGS "-Wl,--no-undefined") TARGET_LINK_LIBRARIES(irc m) @@ -1073,3 +1086,38 @@ INSTALL(TARGETS ${INSTALLTARGS} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}${FTE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}" ) + +ADD_CUSTOM_TARGET(menusys ALL + VERBATIM + COMMAND fteqcc -srcfile "${CMAKE_CURRENT_SOURCE_DIR}/quakec/menusys/menu.src" -o "${CMAKE_CURRENT_BINARY_DIR}/menu.dat" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/menu.dat" "${CMAKE_CURRENT_BINARY_DIR}/menu.lno" + SOURCES + quakec/menusys/menu.src + quakec/menusys/menusys/mitems.qc + quakec/menusys/menusys/mitems_common.qc + quakec/menusys/menusys/mitem_desktop.qc + quakec/menusys/menusys/mitem_exmenu.qc + quakec/menusys/menusys/mitem_edittext.qc + quakec/menusys/menusys/mitem_tabs.qc + quakec/menusys/menusys/mitem_colours.qc + quakec/menusys/menusys/mitem_checkbox.qc + quakec/menusys/menusys/mitem_slider.qc + quakec/menusys/menusys/mitem_combo.qc + quakec/menusys/menusys/mitem_bind.qc + quakec/menusys/menusys/mitem_spinnymodel.qc + quakec/menusys/menu/loadsave.qc + quakec/menusys/menu/newgame.qc + quakec/menusys/menu/options_basic.qc + quakec/menusys/menu/options_effects.qc + quakec/menusys/menu/options_keys.qc + quakec/menusys/menu/options.qc + quakec/menusys/menu/presets.qc + quakec/menusys/menu/servers.qc + quakec/menusys/menu/main.qc + quakec/menusys/menu/options_audio.qc + quakec/menusys/menu/options_configs.qc + quakec/menusys/menu/options_hud.qc + quakec/menusys/menu/options_particles.qc + quakec/menusys/menu/options_video.qc + quakec/menusys/menu/quit.qc +) diff --git a/engine/Makefile b/engine/Makefile index 16dfcb80..d7bf755a 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -19,9 +19,18 @@ else endif ifeq ($(SVNREVISION),) - SVNREVISION:=-DSVNREVISION=$(shell test -d $(BASE_DIR)/../.svn && svnversion $(BASE_DIR) || echo -) + SVN_VERSION:=$(shell test -d $(BASE_DIR)/../.svn && svnversion $(BASE_DIR)) + SVN_DATE:=$(shell test -d $(BASE_DIR)/../.svn && cd $(BASE_DIR) && svn info --show-item last-changed-date --no-newline) + + SVNREVISION= +ifneq (,$(SVN_VERSION)) + SVNREVISION+=-DSVNREVISION=$(SVN_VERSION) endif -MAKE:=$(MAKE) SVNREVISION=$(SVNREVISION) +ifneq (M,$(findstring M,$(SVN_VERSION))) + SVNREVISION+=-DSVNDATE=$(SVN_DATE) +endif +endif +MAKE:=$(MAKE) --no-print-directory SVNREVISION="$(SVNREVISION)" #WHOAMI:=$(shell whoami) diff --git a/engine/client/cd_linux.c b/engine/client/cd_linux.c index a56d43bf..83d2a83a 100644 --- a/engine/client/cd_linux.c +++ b/engine/client/cd_linux.c @@ -126,7 +126,7 @@ void CDAudio_Play(int track) playing = true; - if (!bgmvolume.value) + if (!bgmvolume.value || !mastervolume.value) CDAudio_Pause (); } diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 2897846d..d31d11e2 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -46,6 +46,7 @@ extern cvar_t r_torch; extern cvar_t r_shadows; extern cvar_t r_showbboxes; extern cvar_t gl_simpleitems; +float r_blobshadows; extern cvar_t cl_gibfilter, cl_deadbodyfilter; extern int cl_playerindex; @@ -3087,7 +3088,7 @@ void CLQ1_AddShadow(entity_t *ent) scenetris_t *t; cl_adddecal_ctx_t ctx; - if (!r_shadows.value || !ent->model || (ent->model->type != mod_alias && ent->model->type != mod_halflife)) + if (!r_blobshadows || !ent->model || (ent->model->type != mod_alias && ent->model->type != mod_halflife)) return; s = R_RegisterShader("shadowshader", SUF_NONE, @@ -3148,7 +3149,7 @@ void CLQ1_AddShadow(entity_t *ent) } ctx.t = t; - Vector4Set(ctx.rgbavalue, 0, 0, 0, r_shadows.value); + Vector4Set(ctx.rgbavalue, 0, 0, 0, r_blobshadows); Mod_ClipDecal(cl.worldmodel, shadoworg, ctx.axis[0], ctx.axis[1], ctx.axis[2], radius, 0,0, CL_AddDecal_Callback, &ctx); if (!t->numidx) cl_numstris--; @@ -4151,8 +4152,33 @@ void CL_LinkPacketEntities (void) else { /*bsp model size*/ - VectorAdd(model->mins, ent->origin, absmin); - VectorAdd(model->maxs, ent->origin, absmax); + if (model->type == mod_brush && (state->angles[0]||state->angles[1]||state->angles[2])) + { + int i; + float v; + float max; + //q2 method, works best with origin brushes. + max = 0; + for (i=0 ; i<3 ; i++) + { + v =fabs( model->mins[i]); + if (v > max) + max = v; + v =fabs( model->maxs[i]); + if (v > max) + max = v; + } + for (i=0 ; i<3 ; i++) + { + absmin[i] = ent->origin[i] - max; + absmax[i] = ent->origin[i] + max; + } + } + else + { + VectorAdd(model->mins, ent->origin, absmin); + VectorAdd(model->maxs, ent->origin, absmax); + } cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &ent->pvscache, absmin, absmax); } diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index c8a4d894..259e706d 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -41,7 +41,7 @@ cvar_t cl_netfps = CVAR("cl_netfps", "150"); cvar_t cl_sparemsec = CVARC("cl_sparemsec", "10", CL_SpareMsec_Callback); cvar_t cl_queueimpulses = CVAR("cl_queueimpulses", "0"); cvar_t cl_smartjump = CVAR("cl_smartjump", "1"); -static cvar_t cl_iDrive = CVARFD("cl_iDrive", "1", CVAR_SEMICHEAT, "Effectively releases movement keys when the opposing key is pressed. This avoids dead-time when both keys are pressed. This can be emulated with various scripts, but that's messy."); +cvar_t cl_iDrive = CVARFD("cl_iDrive", "1", CVAR_SEMICHEAT, "Effectively releases movement keys when the opposing key is pressed. This avoids dead-time when both keys are pressed. This can be emulated with various scripts, but that's messy."); cvar_t cl_run = CVARD("cl_run", "0", "Enables autorun, inverting the state of the +speed key."); cvar_t cl_fastaccel = CVARD("cl_fastaccel", "1", "Begin moving at full speed instantly, instead of waiting a frame or so."); extern cvar_t cl_rollspeed; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index f157155a..bd7ca591 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -364,6 +364,9 @@ void CL_UpdateWindowTitle(void) } } +#ifdef __linux__ +#include +#endif void CL_MakeActive(char *gamename) { extern int fs_finds; @@ -409,6 +412,10 @@ void CL_MakeActive(char *gamename) TP_ExecTrigger("f_spawndemo", true); else TP_ExecTrigger("f_spawn", false); + +#ifdef __linux__ + malloc_trim(0); +#endif } /* ================== @@ -1802,6 +1809,7 @@ void CL_ClearState (qboolean gamestart) CL_ResetFog(FOGTYPE_WATER); CL_ResetFog(FOGTYPE_SKYROOM); + cl.gamespeed = 1; cl.protocol_qw = PROTOCOL_VERSION_QW; //until we get an svc_serverdata cl.allocated_client_slots = QWMAX_CLIENTS; #ifndef CLIENTONLY @@ -4896,10 +4904,10 @@ void CL_Init (void) Cmd_AddCommand ("kill", NULL); Cmd_AddCommand ("pause", NULL); - Cmd_AddCommand ("say", CL_Say_f); - Cmd_AddCommand ("me", CL_SayMe_f); - Cmd_AddCommand ("sayone", CL_Say_f); - Cmd_AddCommand ("say_team", CL_SayTeam_f); + Cmd_AddCommandAD ("say", CL_Say_f, Key_EmojiCompletion_c, NULL); + Cmd_AddCommandAD ("me", CL_SayMe_f, Key_EmojiCompletion_c, NULL); + Cmd_AddCommandAD ("sayone", CL_Say_f, Key_EmojiCompletion_c, NULL); + Cmd_AddCommandAD ("say_team", CL_SayTeam_f, Key_EmojiCompletion_c, NULL); #ifdef CLIENTONLY Cmd_AddCommand ("serverinfo", NULL); #else @@ -5892,9 +5900,9 @@ double Host_Frame (double time) cl.gametimemark += time; //if we're at a menu/console/thing - idle = !Key_Dest_Has_Higher(kdm_menu); - idle = ((cls.state == ca_disconnected) || cl.paused) && idle; //idle if we're disconnected/paused and not at a menu - idle |= !vid.activeapp; //always idle when tabbed out +// idle = !Key_Dest_Has_Higher(kdm_menu); +// idle = ((cls.state == ca_disconnected) || cl.paused) && idle; //idle if we're disconnected/paused and not at a menu + idle = !vid.activeapp; //always idle when tabbed out //read packets early and always, so we don't have stuff waiting for reception quite so often. //should smooth out a few things, and increase download speeds. @@ -6453,8 +6461,6 @@ void CL_ExecInitialConfigs(char *resetcommand) Cbuf_AddText ("exec q3config.cfg\n", RESTRICT_LOCAL); else //if (cfg <= def && cfg!=0x7fffffff) Cbuf_AddText ("exec config.cfg\n", RESTRICT_LOCAL); -// else -// Cbuf_AddText ("exec fte.cfg\n", RESTRICT_LOCAL); if (def!=FDEPTH_MISSING) Cbuf_AddText ("exec autoexec.cfg\n", RESTRICT_LOCAL); } @@ -6521,7 +6527,7 @@ void Host_FinishLoading(void) SV_ArgumentOverrides(); #endif - Con_Printf ("\n%s\n", version_string()); + Con_Printf ("\nEngine: %s\n", version_string()); Con_DPrintf("This program is free software; you can redistribute it and/or " "modify it under the terms of the GNU General Public License " diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 4a574048..741d642d 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -3979,7 +3979,7 @@ Con_DPrintf ("CL_SignonReply: %i\n", cls.signon); case 1: cl.sendprespawn = true; SCR_SetLoadingFile("loading data"); - CL_RequestNextDownload(); + CL_RequestNextDownload(); //this sucks, but sometimes mods send csqc-specific messages to us before things are properly inited. if we start doing stuff now then we can minimize the chances of dodgy mods screwing with us. FIXME: warn about receiving csqc messages before begin. break; case 2: diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index 63334755..dafe40f3 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -152,6 +152,20 @@ static void Plug_FreePlugImages(plugin_t *plug) } } +//called before shaders get flushed, to avoid issues later. +void Plug_FreeAllImages(void) +{ + size_t i; + for (i = 0; i < pluginimagearraylen; i++) + { + if (pluginimagearray[i].pic) + { + R_UnloadShader(pluginimagearray[i].pic); + pluginimagearray[i].pic = NULL; + } + } +} + static qhandle_t Plug_Draw_LoadImage(const char *name, int type, const char *script) { int i; diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 4fdd9b10..12ae57fd 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -1188,6 +1188,7 @@ void CL_PredictMovePNum (int seat) } if (i == pe->num_entities && pv->nolocalplayer) { + return; //no player, nothing makes sense any more. from.state = &nullstate; nopred = true; } diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 1ac9d3dd..e9bedc94 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -243,7 +243,7 @@ cvar_t scr_loadingrefresh = CVARD("scr_loadingrefresh", "0", "Force redrawing of cvar_t scr_showloading = CVAR("scr_showloading", "1"); //things to configure the legacy loading screen cvar_t scr_loadingscreen_picture = CVAR("scr_loadingscreen_picture", "gfx/loading"); -cvar_t scr_loadingscreen_aspect = CVARD("scr_loadingscreen_aspect", "0", "Controls the aspect of levelshot images.\n0: Use source image's aspect.\n1: Force 4:3 aspect (ignore image's aspect), for best q3 compat.\n2: Ignore aspect considerations and just smear it over the entire screen."); +cvar_t scr_loadingscreen_aspect = CVARD("scr_loadingscreen_aspect", "0", "Controls the aspect of levelshot images.\n0: Use source image's aspect.\n1: Force 4:3 aspect (ignore image's aspect), for best q3 compat.\n2: Ignore aspect considerations and just smear it over the entire screen.\n-1: Disable levelshot use."); cvar_t scr_loadingscreen_scale = CVAR("scr_loadingscreen_scale", "1"); cvar_t scr_loadingscreen_scale_limit = CVAR("scr_loadingscreen_scale_limit", "2"); @@ -2001,7 +2001,7 @@ void SCR_DrawLoading (qboolean opaque) int qdepth = COM_FDepthFile(qname, true); int h2depth = COM_FDepthFile("gfx/menu/loading.lmp", true); - if (!(qdepth < h2depth || h2depth > FDEPTH_MISSING)) + if (qdepth < h2depth && h2depth != FDEPTH_MISSING) { //hexen2 files. //hexen2 has some fancy sliders built into its graphics in specific places. so this is messy. pic = R2D_SafeCachePic ("gfx/menu/loading.lmp"); @@ -2225,7 +2225,7 @@ void SCR_ImageName (const char *mapname) strcpy(levelshotname, "levelshots/"); COM_FileBase(mapname, levelshotname + strlen(levelshotname), sizeof(levelshotname)-strlen(levelshotname)); - if (qrenderer) + if (qrenderer && scr_loadingscreen_aspect.ival >= 0) { R_LoadHiResTexture(levelshotname, NULL, IF_NOWORKER|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP); @@ -2468,7 +2468,7 @@ static void SCR_ScreenShot_f (void) Con_Printf (CON_ERROR "Couldn't get colour buffer for screenshot\n"); } -void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt, qboolean no2d) +void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt, qboolean no2d, qboolean hdr) { int width, height; void *buf; @@ -2489,7 +2489,7 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum upload { r_refdef.warndraw = true; Q_strncpyz(r_refdef.rt_destcolour[0].texname, "megascreeny", sizeof(r_refdef.rt_destcolour[0].texname)); - /*vid.framebuffer =*/R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, fbwidth, fbheight, 1, RT_IMAGEFLAGS); + /*vid.framebuffer =*/R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, fbwidth, fbheight, (hdr&&sh_config.texfmt[PTI_RGBA16F])?PTI_RGBA16F:PTI_RGBA8, RT_IMAGEFLAGS); BE_RenderToTextureUpdate2d(true); } else @@ -2639,7 +2639,7 @@ static void SCR_ScreenShot_Mega_f(void) r_refdef.stereomethod = STEREO_LEFTONLY; } - buffers[buf] = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride[buf], &fmt[buf], false); + buffers[buf] = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride[buf], &fmt[buf], false, false); width[buf] = fbwidth; height[buf] = fbheight; @@ -2754,7 +2754,7 @@ static void SCR_ScreenShot_VR_f(void) r_refdef.eyeoffset[0] = sin(ang) * r_stereo_separation.value * 0.5; r_refdef.eyeoffset[1] = cos(ang) * r_stereo_separation.value * 0.5; r_refdef.eyeoffset[2] = 0; - buf = SCR_ScreenShot_Capture(width, height, &stride, &fmt, true); + buf = SCR_ScreenShot_Capture(width, height, &stride, &fmt, true, false); switch(fmt) { case TF_BGRA32: @@ -2788,7 +2788,7 @@ static void SCR_ScreenShot_VR_f(void) r_refdef.eyeoffset[0] *= -1; r_refdef.eyeoffset[1] *= -1; r_refdef.eyeoffset[2] = 0; - buf = SCR_ScreenShot_Capture(width, height, &stride, &fmt, true); + buf = SCR_ScreenShot_Capture(width, height, &stride, &fmt, true, false); switch(fmt) { case TF_BGRA32: @@ -2901,7 +2901,7 @@ void SCR_ScreenShot_Cubemap_f(void) mips.type = PTI_CUBE; mips.encoding = 0; mips.extrafree = NULL; - mips.mipcount = 6; + mips.mipcount = 1; bb=0; for (i = 0; i < 6; i++) @@ -2909,7 +2909,7 @@ void SCR_ScreenShot_Cubemap_f(void) VectorCopy(sides[i].angle, cl.playerview->simangles); VectorCopy(cl.playerview->simangles, cl.playerview->viewangles); - facedata = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true); + facedata = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true, true); if (!facedata) break; if (!i) @@ -2941,6 +2941,10 @@ void SCR_ScreenShot_Cubemap_f(void) } if (i == 6) { + qboolean pixelformats[PTI_MAX] = {0}; + pixelformats[PTI_E5BGR9] = true; + Image_ChangeFormat(&mips, pixelformats, mips.encoding, fname); + Q_snprintfz(filename, sizeof(filename), "textures/%s", fname); COM_DefaultExtension (filename, ext, sizeof(filename)); #ifdef IMAGEFMT_KTX @@ -2975,9 +2979,8 @@ void SCR_ScreenShot_Cubemap_f(void) else Con_Printf ("%s: Unknown format %s\n", Cmd_Argv(0), filename); } - while (i-- > 0) - if (mips.mip[i].needfree) - BZ_Free(mips.mip[i].data); + if (mips.mip[0].needfree) + BZ_Free(mips.mip[0].data); } else { @@ -2986,7 +2989,7 @@ void SCR_ScreenShot_Cubemap_f(void) VectorCopy(sides[i].angle, cl.playerview->simangles); VectorCopy(cl.playerview->simangles, cl.playerview->viewangles); - buffer = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true); + buffer = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true, false); if (buffer) { Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh); diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 8a3443e5..a4fd6b8b 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -1146,7 +1146,7 @@ void CL_ParseTEnt (void) if (type < countof(te_names)) Con_Printf(" te_%s\n", te_names[type]); else - Con_Printf(" te_%i\n", type); + Con_Printf(" te_unknown_%i\n", type); } switch (type) diff --git a/engine/client/client.h b/engine/client/client.h index fef96e71..64dac411 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -309,7 +309,7 @@ typedef struct #define LFLAG_ORTHO (1<<11) //sun-style -light #define LFLAG_INTERNAL (LFLAG_LIGHTMAP|LFLAG_FLASHBLEND) //these are internal to FTE, and never written to disk (ie: .rtlights files shouldn't contain these) -#define LFLAG_DYNAMIC (LFLAG_LIGHTMAP | LFLAG_FLASHBLEND | LFLAG_NORMALMODE | LFLAG_REALTIMEMODE) +#define LFLAG_DYNAMIC (LFLAG_LIGHTMAP | LFLAG_FLASHBLEND | LFLAG_NORMALMODE) typedef struct dlight_s { @@ -1619,7 +1619,7 @@ void Validation_FlushFileList(void); void Validation_CheckIfResponse(char *text); void Validation_DelatchRulesets(void); void InitValidation(void); -void Validation_IncludeFile(char *filename, char *file, int filelen); +void Validation_FileLoaded(const char *filename, const qbyte *filedata, size_t filesize); void Validation_Auto_Response(int playernum, char *s); extern qboolean f_modified_particles; diff --git a/engine/client/console.c b/engine/client/console.c index 461c3d2a..c102da04 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -580,8 +580,13 @@ void Con_History_Load(void) } void Con_History_Save(void) { - vfsfile_t *file = FS_OpenVFS("conhistory.txt", "wb", FS_ROOT); + vfsfile_t *file; int line; + + if (!FS_GameIsInitialised()) + return; + + file = FS_OpenVFS("conhistory.txt", "wb", FS_ROOT); if (file) { line = edit_line - CON_EDIT_LINES_MASK; @@ -2958,7 +2963,7 @@ void Con_DrawConsole (int lines, qboolean noback) shader = R2D_SafeCachePic("tiprawimg"); shader->defaulttextures->base = Image_TextureIsValid(strtoull(key, NULL, 0)); } - if (shader && shader->defaulttextures->base) + if (shader && shader->defaulttextures->base && shader->defaulttextures->base->status == TEX_LOADED && ((shader->defaulttextures->base->flags&IF_TEXTYPEMASK) == (PTI_2D<width = shader->defaulttextures->base->width; shader->height = shader->defaulttextures->base->height; @@ -2973,6 +2978,8 @@ void Con_DrawConsole (int lines, qboolean noback) shader->height = 240; } } + else + shader = NULL; key = Info_ValueForKey(info, "modelviewer"); if (*key) { diff --git a/engine/client/image.c b/engine/client/image.c index f65eeba1..dfced236 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -3678,8 +3678,8 @@ static void *ReadPSDFile(qbyte *buf, size_t len, const char *fname, int *outwidt struct xcf_s { const qbyte *filestart; - size_t filesize; - size_t offset; + qofs_t filesize; + qofs_t offset; unsigned int version; qbyte compression; @@ -3692,7 +3692,7 @@ struct xcf_s size_t *layeroffsets; qbyte *flat; }; -static unsigned int XCF_ReadData(struct xcf_s *f, qbyte *out, size_t len) +static size_t XCF_ReadData(struct xcf_s *f, qbyte *out, size_t len) { size_t avail; if (f->offset >= f->filesize) @@ -3731,7 +3731,7 @@ static float XCF_ReadFloat(struct xcf_s *f) u.u = (w[0]<<24)|(w[1]<<16)|(w[2]<<8)|(w[3]<<0); return u.f; } -static size_t XCF_ReadOffset(struct xcf_s *f) +static qofs_t XCF_ReadOffset(struct xcf_s *f) { //XCF is natively big-endian. qbyte w[8]; size_t h, l; @@ -3740,7 +3740,7 @@ static size_t XCF_ReadOffset(struct xcf_s *f) XCF_ReadData(f, w, sizeof(w)); h = (w[0]<<24)|(w[1]<<16)|(w[2]<<8)|(w[3]<<0); l = (w[4]<<24)|(w[5]<<16)|(w[6]<<8)|(w[7]<<0); - return (h<<32)|l; + return qofs_Make(l, h); } return XCF_Read32(f); } @@ -3878,7 +3878,7 @@ static struct xcf_heirachy_s XCF_ReadHeirachy(struct xcf_s *f) { struct xcf_heirachy_s ctx; uint32_t x, y, lw, lh; - size_t ofs, tofs; + qofs_t ofs, tofs; if (!f->offset) { memset(&ctx, 0, sizeof(ctx)); @@ -4593,6 +4593,58 @@ static void Image_LoadTexture_Failed(void *ctx, void *data, size_t a, size_t b) texid_t tex = ctx; tex->status = TEX_FAILED; } +static void Image_FixupImageSize(texid_t tex, unsigned int w, unsigned int h) +{ + tex->width = w; + tex->height = h; + + //ezhud breaks without this. I assume other things will too. this is why you shouldn't depend upon querying an image's size. + if (!strncmp(tex->ident, "gfx/", 4)) + { + size_t lumpsize; + qbyte lumptype; + qpic_t *pic = W_GetLumpName(tex->ident+4, &lumpsize, &lumptype); + if (pic && lumptype == TYP_QPIC && lumpsize >= 8) + { + w = LittleLong(pic->width); + h = LittleLong(pic->height); + if (lumpsize == 8 + w*h) + { + tex->width = w; + tex->height = h; + } + } + else + { + vfsfile_t *f; + const char *ext = COM_GetFileExtension(tex->ident, NULL); + if (!strcmp(ext, ".lmp")) + f = FS_OpenVFS(tex->ident, "rb", FS_GAME); + else if (!*ext) + { + char nname[MAX_QPATH+4]; + Q_snprintfz(nname, sizeof(nname), "%s.lmp", tex->ident); + f = FS_OpenVFS(nname, "rb", FS_GAME); + } + else + f = NULL; + if (f) + { + unsigned int wh[2]; + size_t size = VFS_GETLEN(f); + VFS_READ(f, wh, 8); + VFS_CLOSE(f); + + if (size == 8+wh[0]*wh[1]) + { + tex->width = wh[0]; + tex->height = wh[1]; + } + } + } + } + //FIXME: check loaded wad files too. +} static void Image_LoadTextureMips(void *ctx, void *data, size_t a, size_t b) { int i; @@ -4657,20 +4709,6 @@ static void Image_LoadTextureMips(void *ctx, void *data, size_t a, size_t b) if (mips->extrafree) BZ_Free(mips->extrafree); BZ_Free(mips); - - //ezhud breaks without this. I assume other things will too. this is why you shouldn't depend upon querying an image's size. - if (!strncmp(tex->ident, "gfx/", 4)) - { - size_t lumpsize; - qbyte lumptype; - qpic_t *pic = W_GetLumpName(tex->ident+4, &lumpsize, &lumptype); - if (pic && lumptype == TYP_QPIC && lumpsize == 8 + pic->width*pic->height) - { - tex->width = pic->width; - tex->height = pic->height; - } - } - //FIXME: check loaded wad files too. } #endif @@ -6632,6 +6670,86 @@ qbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_ return NULL; } +static void Image_MipMap1X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) +{ + int i, j; + qbyte *inrow; + + int rowwidth = inwidth; //rowwidth is the byte width of the input + inrow = in; + + //mips round down, except for when the input is 1. which bugs out. + if (inwidth <= 1 && inheight <= 1) + out[0] = in[0]; + else if (inheight <= 1) + { + //single row, don't peek at the next + for (in = inrow, j=0 ; j>1; + } + else if (inwidth <= 1) + { + //single colum, peek only at this pixel + for (i=0 ; i>1; + } + else + { + for (i=0 ; i>2; + } +} + +static void Image_MipMap2X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) +{ + int i, j; + qbyte *inrow; + + int rowwidth = inwidth*2; //rowwidth is the byte width of the input + inrow = in; + + //mips round down, except for when the input is 1. which bugs out. + if (inwidth <= 1 && inheight <= 1) + { + out[0] = in[0]; + out[1] = in[1]; + } + else if (inheight <= 1) + { + //single row, don't peek at the next + for (in = inrow, j=0 ; j>1; + out[1] = (in[1] + in[3])>>1; + } + } + else if (inwidth <= 1) + { + //single colum, peek only at this pixel + for (i=0 ; i>1; + out[1] = (in[1] + in[rowwidth+1])>>1; + } + } + } + else + { + for (i=0 ; i>2; + out[1] = (in[1] + in[3] + in[rowwidth+1] + in[rowwidth+3])>>2; + } + } + } +} + static void Image_MipMap3X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) { int i, j; @@ -6990,38 +7108,6 @@ static void Image_MipMap4X32F (float *in, int inwidth, int inheight, float *out, } } -static void Image_MipMap1X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) -{ - int i, j; - qbyte *inrow; - - int rowwidth = inwidth; //rowwidth is the byte width of the input - inrow = in; - - //mips round down, except for when the input is 1. which bugs out. - if (inwidth <= 1 && inheight <= 1) - out[0] = in[0]; - else if (inheight <= 1) - { - //single row, don't peek at the next - for (in = inrow, j=0 ; j>1; - } - else if (inwidth <= 1) - { - //single colum, peek only at this pixel - for (i=0 ; i>1; - } - else - { - for (i=0 ; i>2; - } -} - static qbyte Image_BlendPalette_2(qbyte a, qbyte b) { return a; @@ -7101,6 +7187,9 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags) } return; case PTI_R8: + case PTI_R8_SNORM: + case PTI_L8: + case PTI_L8_SRGB: for (mip = mips->mipcount; mip < 32; mip++) { mips->mip[mip].width = mips->mip[mip-1].width >> 1; @@ -7119,6 +7208,29 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags) Image_MipMap1X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); mips->mipcount = mip+1; } + return; + case PTI_RG8: + case PTI_RG8_SNORM: + case PTI_L8A8: + case PTI_L8A8_SRGB: + for (mip = mips->mipcount; mip < 32; mip++) + { + mips->mip[mip].width = mips->mip[mip-1].width >> 1; + mips->mip[mip].height = mips->mip[mip-1].height >> 1; + mips->mip[mip].depth = 1; + if (mips->mip[mip].width < 1 && mips->mip[mip].height < 1) + break; + if (mips->mip[mip].width < 1) + mips->mip[mip].width = 1; + if (mips->mip[mip].height < 1) + mips->mip[mip].height = 1; + mips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height * 2; + mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize); + mips->mip[mip].needfree = true; + + Image_MipMap2X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); + mips->mipcount = mip+1; + } return; case PTI_RGBA32F: for (mip = mips->mipcount; mip < 32; mip++) @@ -7195,7 +7307,7 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags) mips->mip[mip].width = 1; if (mips->mip[mip].height < 1) mips->mip[mip].height = 1; - mips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height * sizeof(qbyte)*4; + mips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height * sizeof(qbyte)*3; mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize); mips->mip[mip].needfree = true; @@ -7695,6 +7807,28 @@ static void Image_Tr_NoTransform(struct pendingtextureinfo *mips, int dummy) { } +//may operate in place +static void Image_Tr_RGBX8toPaletted(struct pendingtextureinfo *mips, int dummy) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + qbyte *in = mips->mip[mip].data; + qbyte *out = mips->mip[mip].data; + unsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth; + unsigned short tmp; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p); + } + mips->mip[mip].datasize = p*sizeof(*out); + + for(; p-->0; in += 4) + *out++ = GetPaletteIndexNoFB(in[0], in[1], in[2]); + } +} + //may operate in place static void Image_Tr_8888toLuminence(struct pendingtextureinfo *mips, int channels) { //channels==1?L8 @@ -10775,49 +10909,15 @@ static struct {PTI_RGBA32F, PTI_RGB32F, Image_Tr_DropBytes, (16<<16)|12, true}, {PTI_RG8, PTI_RGBX8, Image_Tr_RG8ToRGXX8}, + {PTI_RGBX8, PTI_P8, Image_Tr_RGBX8toPaletted}, }; -void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename) +void Image_ChangeFormat(struct pendingtextureinfo *mips, qboolean *allowedformats, uploadfmt_t origfmt, const char *imagename) { - int mip; - - if (flags & IF_PALETTIZE) - { - Image_DecompressFormat(mips, NULL); //force-decompress it, so that we can palettise it. - if (mips->encoding == PTI_RGBX8 || mips->encoding == PTI_RGBA8) - { - mips->encoding = PTI_P8; - for (mip = 0; mip < mips->mipcount; mip++) - { - unsigned int i; - unsigned char *out; - unsigned char *in; - void *needfree = NULL; - - in = mips->mip[mip].data; - if (!in) - continue; - if (mips->mip[mip].needfree) - out = in; - else - { - needfree = in; - out = BZ_Malloc(mips->mip[mip].width*mips->mip[mip].height*sizeof(*out)); - mips->mip[mip].data = out; - } - mips->mip[mip].datasize = mips->mip[mip].width*mips->mip[mip].height; - mips->mip[mip].needfree = true; - - for (i = 0; i < mips->mip[mip].width*mips->mip[mip].height; i++, in+=4) - out[i] = GetPaletteIndexNoFB(in[0], in[1], in[2]); - - if (needfree) - BZ_Free(needfree); - } - } - } + if (!allowedformats) + allowedformats = sh_config.texfmt; //if that format isn't supported/desired, try converting it. - if (sh_config.texfmt[mips->encoding]) + if (allowedformats[mips->encoding]) { if (mips->encoding >= PTI_ASTC_FIRST && mips->encoding <= PTI_ASTC_LAST) return; //ignore texture_allow_block_padding for astc. @@ -10837,7 +10937,7 @@ void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, upl //when the format can't be used, decompress it if its one of those awkward compressed formats. Image_DecompressFormat(mips, imagename); - if (sh_config.texfmt[mips->encoding]) + if (allowedformats[mips->encoding]) return; //okay, that got it. @@ -10849,7 +10949,7 @@ void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, upl { if (formattransforms[i].src == src) { - if (sh_config.texfmt[formattransforms[i].dest]) + if (allowedformats[formattransforms[i].dest]) { if (formattransforms[i].onebitalpha && !onebitokay) { @@ -10873,7 +10973,7 @@ void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, upl for (j = 0; j < countof(formattransforms); j++) { if (formattransforms[j].src == formattransforms[i].dest) - if (sh_config.texfmt[formattransforms[j].dest]) + if (allowedformats[formattransforms[j].dest]) { first = i; sec = j; @@ -10896,11 +10996,23 @@ void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, upl mips->encoding = formattransforms[sec].dest; } - if (sh_config.texfmt[mips->encoding]) + if (allowedformats[mips->encoding]) return; //okay, that got it. } } +static void Image_ChangeFormatFlags(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename) +{ + if (flags & IF_PALETTIZE) + { + qboolean p8only[PTI_MAX] = {0}; + p8only[PTI_P8] = true; + Image_ChangeFormat(mips, p8only, origfmt, imagename); + } + else + Image_ChangeFormat(mips, sh_config.texfmt, origfmt, imagename); +} + //resamples and depalettes as required //ALWAYS frees rawdata, even on failure (but never mips). static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flags, void *rawdata, void *palettedata, int imgwidth, int imgheight, uploadfmt_t fmt, qboolean freedata) @@ -11315,7 +11427,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag rgbadata = BZ_Malloc(imgwidth * imgheight*4); for (i = 0; i < imgwidth * imgheight; i++) { - static const int ColorIndex[16] = {0, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 199, 207, 223, 231}; + static const int ColorIndex[16] = {0x00, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf, 0xbf, 0xc7, 0xcf, 0xdf, 0xe7}; static const unsigned ColorPercent[16] = {25, 51, 76, 102, 114, 127, 140, 153, 165, 178, 191, 204, 216, 229, 237, 247}; qbyte p = ((qbyte*)rawdata)[i]; rgbadata[i] = d_8to24rgbtable[ColorIndex[p>>4]] & 0x00ffffff; @@ -11912,7 +12024,7 @@ struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname memmove(mips->mip, mips->mip+i, sizeof(*mips->mip)*mips->mipcount); } - Image_ChangeFormat(mips, flags, TF_INVALID, fname); + Image_ChangeFormatFlags(mips, flags, TF_INVALID, fname); return mips; } @@ -11953,7 +12065,7 @@ struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname if (Image_GenMip0(mips, flags, rgbadata, NULL, imgwidth, imgheight, format, true)) { Image_GenerateMips(mips, flags); - Image_ChangeFormat(mips, flags, format, fname); + Image_ChangeFormatFlags(mips, flags, format, fname); BZ_Free(filedata); return mips; } @@ -12230,11 +12342,9 @@ static qboolean Image_LoadRawTexture(texid_t tex, unsigned int flags, void *rawd return false; } Image_GenerateMips(mips, flags); - Image_ChangeFormat(mips, flags, fmt, tex->ident); - - tex->width = imgwidth; - tex->height = imgheight; + Image_ChangeFormatFlags(mips, flags, fmt, tex->ident); + Image_FixupImageSize(tex, imgwidth, imgheight); if (flags & IF_NOWORKER) Image_LoadTextureMips(tex, mips, 0, 0); else @@ -12252,8 +12362,7 @@ qboolean Image_LoadTextureFromMemory(texid_t tex, int flags, const char *iname, BZ_Free(tex->fallbackdata); tex->fallbackdata = NULL; - tex->width = mips->mip[0].width; - tex->height = mips->mip[0].height; + Image_FixupImageSize(tex, mips->mip[0].width, mips->mip[0].height); if ((flags & IF_NOWORKER) || Sys_IsMainThread()) Image_LoadTextureMips(tex, mips, 0, 0); else @@ -12606,9 +12715,7 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t if (mips) { - tex->width = mips->mip[0].width; - tex->height = mips->mip[0].height; - + Image_FixupImageSize(tex, mips->mip[0].width, mips->mip[0].height); if (tex->flags & IF_NOWORKER) Image_LoadTextureMips(tex, mips, 0, 0); else @@ -12980,7 +13087,7 @@ void Image_Upload (texid_t tex, uploadfmt_t fmt, void *data, void *palette, in if (!Image_GenMip0(&mips, flags, data, palette, width, height, fmt, false)) return; Image_GenerateMips(&mips, flags); - Image_ChangeFormat(&mips, flags, fmt, tex->ident); + Image_ChangeFormatFlags(&mips, flags, fmt, tex->ident); rf->IMG_LoadTextureMips(tex, &mips); tex->format = fmt; tex->width = width; @@ -13148,7 +13255,8 @@ void Image_List_f(void) failed++; continue; } - Con_Printf("^[\\imgptr\\%#"PRIxSIZE"^]", (size_t)tex); + if (((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_2D) + Con_Printf("^[\\imgptr\\%#"PRIxSIZE"^]", (size_t)tex); if (tex->subpath) Con_Printf("^h(%s)^h", tex->subpath); Con_DLPrintf(1, " %x", tex->flags); @@ -13160,10 +13268,10 @@ void Image_List_f(void) while((bullshit=strchr(defuck, '\\'))) *bullshit = '/'; -// if ((tex->flags&(IF_CLAMP|IF_PALETTIZE)) == 0) + if (((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_2D || tex->format == PTI_P8) Con_Printf("^[%s\\tip\\%s/%s\\tipimgptr\\%#"PRIxSIZE"^]: ", tex->ident, defuck, fname, (size_t)tex); -// else -// Con_Printf("^[%s\\tip\\%s/%s^]: ", tex->ident, defuck, fname); + else + Con_Printf("^[%s\\tip\\%s/%s^]: ", tex->ident, defuck, fname); } else { @@ -13324,6 +13432,7 @@ void Image_Shutdown(void) image_t *tex; int i = 0, j = 0; Cmd_RemoveCommand("r_imagelist"); + Cmd_RemoveCommand("r_imageformats"); while (imagelist) { tex = imagelist; diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index e4dce32d..e6a5c554 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -790,7 +790,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frame if (m_accel.value) { - if (m_accel_style.ival) + if (m_accel_style.ival && frametime) { float accelsens = sensitivity.value*in_sensitivityscale; float mousespeed = (sqrt (mx * mx + my * my)) / (1000.0f * (float) frametime); diff --git a/engine/client/in_win.c b/engine/client/in_win.c index 82fd7fe0..05f35f15 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -627,7 +627,12 @@ static void INS_DeactivateMouse (void) ClipCursor (NULL); ReleaseCapture (); - SetCursorPos(window_rect.left+mousecursor_x, window_rect.top+mousecursor_y); + if (!vid.forcecursor) + { + vid.forcecursor = true; + vid.forcecursorpos[0] = mousecursor_x; + vid.forcecursorpos[1] = mousecursor_y; + } } mouseactive = false; @@ -729,6 +734,14 @@ void INS_UpdateGrabs(int fullscreen, int activeapp) INS_ActivateMouse(); else INS_DeactivateMouse(); + + + if (vid.forcecursor && !mouseactive) + { + vid.forcecursor = false; + if (activeapp) + SetCursorPos(window_rect.left+vid.forcecursorpos[0], window_rect.top+vid.forcecursorpos[1]); + } } diff --git a/engine/client/keys.c b/engine/client/keys.c index 3c0a92c3..8af5e3f7 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -49,7 +49,7 @@ int key_count; // incremented every key event int key_bindmaps[2]; char *keybindings[K_MAX][KEY_MODIFIERSTATES]; -qbyte bindcmdlevel[K_MAX][KEY_MODIFIERSTATES]; +qbyte bindcmdlevel[K_MAX][KEY_MODIFIERSTATES]; //should be a struct, but not due to 7 bytes wasted per on 64bit machines qboolean consolekeys[K_MAX]; // if true, can't be rebound while in console int keyshift[K_MAX]; // key to map to if shift held down in console int key_repeats[K_MAX]; // if > 1, it is autorepeating @@ -1273,74 +1273,166 @@ void Key_ConsoleRelease(console_t *con, int key, unsigned int unicode) #endif } -const char *Key_Demoji(char *buffer, size_t buffersize, const char *in) -{ - static const struct - { - const char *pattern; - const char *repl; - } emoji[] = - { - //https://www.webpagefx.com/tools/emoji-cheat-sheet/ -// {":)", "\xE2\x98\xBA"}, +static qbyte *emojidata = NULL; +static const qbyte *builtinemojidata = +// "\x02\x03" ":)" "\xE2\x98\xBA" #ifdef QUAKEHUD - {":sg:", "\xEE\x84\x82"}, - {":ssg:", "\xEE\x84\x83"}, - {":ng:", "\xEE\x84\x84"}, - {":sng:", "\xEE\x84\x85"}, - {":gl:", "\xEE\x84\x86"}, - {":rl:", "\xEE\x84\x87"}, - {":lg:", "\xEE\x84\x88"}, + "\x04\x03" ":sg:" "\xEE\x84\x82" + "\x05\x03" ":ssg:" "\xEE\x84\x83" + "\x04\x03" ":ng:" "\xEE\x84\x84" + "\x05\x03" ":sng:" "\xEE\x84\x85" + "\x04\x03" ":gl:" "\xEE\x84\x86" + "\x04\x03" ":rl:" "\xEE\x84\x87" + "\x04\x03" ":lg:" "\xEE\x84\x88" - {":sg2:", "\xEE\x84\x92"}, - {":ssg2:", "\xEE\x84\x93"}, - {":ng2:", "\xEE\x84\x94"}, - {":sng2:", "\xEE\x84\x95"}, - {":gl2:", "\xEE\x84\x96"}, - {":rl2:", "\xEE\x84\x97"}, - {":lg2:", "\xEE\x84\x98"}, + "\x05\x03" ":sg2:" "\xEE\x84\x92" + "\x06\x03" ":ssg2:" "\xEE\x84\x93" + "\x05\x03" ":ng2:" "\xEE\x84\x94" + "\x06\x03" ":sng2:" "\xEE\x84\x95" + "\x05\x03" ":gl2:" "\xEE\x84\x96" + "\x05\x03" ":rl2:" "\xEE\x84\x97" + "\x05\x03" ":lg2:" "\xEE\x84\x98" - {":shells:", "\xEE\x84\xA0"}, - {":nails:", "\xEE\x84\xA1"}, - {":rocket:", "\xEE\x84\xA2"}, - {":cells:", "\xEE\x84\xA3"}, - {":ga:", "\xEE\x84\xA4"}, - {":ya:", "\xEE\x84\xA5"}, - {":ra:", "\xEE\x84\xA6"}, + "\x08\x03" ":shells:" "\xEE\x84\xA0" + "\x07\x03" ":nails:" "\xEE\x84\xA1" + "\x08\x03" ":rocket:" "\xEE\x84\xA2" + "\x07\x03" ":cells:" "\xEE\x84\xA3" + "\x04\x03" ":ga:" "\xEE\x84\xA4" + "\x04\x03" ":ya:" "\xEE\x84\xA5" + "\x04\x03" ":ra:" "\xEE\x84\xA6" - {":key1:", "\xEE\x84\xB0"}, - {":key2:", "\xEE\x84\xB1"}, - {":ring:", "\xEE\x84\xB2"}, - {":pent:", "\xEE\x84\xB3"}, - {":suit:", "\xEE\x84\xB4"}, - {":quad:", "\xEE\x84\xB5"}, - {":sigil1:", "\xEE\x84\xB6"}, - {":sigil2:", "\xEE\x84\xB7"}, - {":sigil3:", "\xEE\x84\xB8"}, - {":sigil4:", "\xEE\x84\xB9"}, + "\x06\x03" ":key1:" "\xEE\x84\xB0" + "\x06\x03" ":key2:" "\xEE\x84\xB1" + "\x06\x03" ":ring:" "\xEE\x84\xB2" + "\x06\x03" ":pent:" "\xEE\x84\xB3" + "\x06\x03" ":suit:" "\xEE\x84\xB4" + "\x06\x03" ":quad:" "\xEE\x84\xB5" + "\x08\x03" ":sigil1:" "\xEE\x84\xB6" + "\x08\x03" ":sigil2:" "\xEE\x84\xB7" + "\x08\x03" ":sigil3:" "\xEE\x84\xB8" + "\x08\x03" ":sigil4:" "\xEE\x84\xB9" - {":face1:", "\xEE\x85\x80"}, - {":face_p1:", "\xEE\x85\x81"}, - {":face2:", "\xEE\x85\x82"}, - {":face_p2:", "\xEE\x85\x83"}, - {":face3:", "\xEE\x85\x84"}, - {":face_p3:", "\xEE\x85\x85"}, - {":face4:", "\xEE\x85\x86"}, - {":face_p4:", "\xEE\x85\x87"}, - {":face5:", "\xEE\x85\x88"}, - {":face_p5:", "\xEE\x85\x89"}, - {":face_invis:", "\xEE\x85\x8A"}, - {":face_invul2:", "\xEE\x85\x8B"}, - {":face_inv2:", "\xEE\x85\x8C"}, - {":face_quad:", "\xEE\x85\x8D"}, + "\x07\x03" ":face1:" "\xEE\x85\x80" + "\x09\x03" ":face_p1:" "\xEE\x85\x81" + "\x07\x03" ":face2:" "\xEE\x85\x82" + "\x09\x03" ":face_p2:" "\xEE\x85\x83" + "\x07\x03" ":face3:" "\xEE\x85\x84" + "\x09\x03" ":face_p3:" "\xEE\x85\x85" + "\x07\x03" ":face4:" "\xEE\x85\x86" + "\x09\x03" ":face_p4:" "\xEE\x85\x87" + "\x07\x03" ":face5:" "\xEE\x85\x88" + "\x09\x03" ":face_p5:" "\xEE\x85\x89" + "\x0c\x03" ":face_invis:" "\xEE\x85\x8A" + "\x0d\x03" ":face_invul2:" "\xEE\x85\x8B" + "\x0b\x03" ":face_inv2:" "\xEE\x85\x8C" + "\x0b\x03" ":face_quad:" "\xEE\x85\x8D" #endif - }; + ""; +static void Key_LoadEmojiList(void) +{ + qbyte line[1024]; + char nam[64]; + char rep[64]; + vfsfile_t *f; + char *json = FS_MallocFile("emoji.json", FS_GAME, NULL); //https://unicodey.com/emoji-data/emoji.json + + emojidata = Z_StrDup(builtinemojidata); + if (json) + { + char *unified; + for (unified = json; (unified = strstr(unified, ",\"unified\":\"")); ) + { + int i = 0; + char *t; + char *sn; + unsigned int u; + unified += 12; + t = unified; + //do + //{ + u = strtol(t, &t, 16); + i += utf8_encode(rep+i, u, countof(rep)-i); + //} while (i < countof(rep) && *t++ == '-'); + if (*t!='\"') + continue; + rep[i] = 0; + + sn = strstr(unified, "\"short_names\":["); + if (sn) + { + sn += 15; + while (sn && *sn == '\"') + { + sn = COM_ParseTokenOut(sn, NULL, nam+1, sizeof(nam)-1, NULL); + nam[0] = ':'; + Q_strncatz(nam, ":", sizeof(nam)); + line[0] = strlen(nam); + line[1] = strlen(rep); + strcpy(line+2, nam); + strcpy(line+2+line[0], rep); + Z_StrCat((char**)&emojidata, line); + } + } + } + FS_FreeFile(json); + } + + f = FS_OpenVFS("emoji.lst", "rb", FS_GAME); + if (f) + { + qbyte line[1024]; + char nam[64]; + char rep[64]; + while (VFS_GETS(f, line, sizeof(line))) + { + COM_ParseTokenOut(COM_ParseTokenOut(line, NULL, nam, sizeof(nam), NULL), NULL, rep, sizeof(rep), NULL); + if (!*nam || !*rep) + continue; //next line then, I guess. + line[0] = strlen(nam); + line[1] = strlen(rep); + strcpy(line+2, nam); + strcpy(line+2+line[0], rep); + Z_StrCat((char**)&emojidata, line); + } + VFS_CLOSE(f); + } +} +void Key_EmojiCompletion_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx) +{ + char guess[256]; + char repl[256]; + size_t ofs, len; + if (!emojidata) + Key_LoadEmojiList(); + len = strlen(partial); + for (ofs = 0; emojidata[ofs]; ) + { + if (len <= emojidata[ofs+0]) + if (!strncmp(partial, &emojidata[ofs+2], len)) + { + memcpy(guess, &emojidata[ofs+2], emojidata[ofs+0]); + guess[emojidata[ofs+0]] = 0; + + memcpy(repl, &emojidata[ofs+2]+emojidata[ofs+0], emojidata[ofs+1]); + repl[emojidata[ofs+1]] = 0; + ctx->cb(guess, NULL, NULL, ctx); + } + ofs += 2+emojidata[ofs+0]+emojidata[ofs+1]; + } +} + +const char *Key_Demoji(char *buffer, size_t buffersize, const char *in) +{ char *estart = strchr(in, ':'); - size_t i; + size_t ofs; char *out = buffer, *outend = buffer+buffersize-1; if (!estart) return in; + + if (!emojidata) + Key_LoadEmojiList(); + for(; estart; ) { if (out + (estart-in) >= outend) @@ -1349,21 +1441,22 @@ const char *Key_Demoji(char *buffer, size_t buffersize, const char *in) out += estart-in; in = estart; - for (i = 0; i < countof(emoji); i++) + for (ofs = 0; emojidata[ofs]; ) { - if (!strncmp(in, emoji[i].pattern, strlen(emoji[i].pattern))) + if (!strncmp(in, &emojidata[ofs+2], emojidata[ofs+0])) break; //its this one! + ofs += 2+emojidata[ofs+0]+emojidata[ofs+1]; } - if (i < countof(emoji)) + if (emojidata[ofs]) { - if (out + strlen(emoji[i].repl) >= outend) + if (out + emojidata[ofs+1] >= outend) { in = ""; //no half-emoji break; } - in += strlen(emoji[i].pattern); - memcpy(out, emoji[i].repl, strlen(emoji[i].repl)); - out += strlen(emoji[i].repl); + in += emojidata[ofs+0]; + memcpy(out, &emojidata[ofs+2]+emojidata[ofs+0], emojidata[ofs+1]); + out += emojidata[ofs+1]; estart = strchr(in, ':'); } else @@ -2194,6 +2287,7 @@ the K_* names are matched up. */ int Key_StringToKeynum (const char *str, int *modifier) { + int k; keyname_t *kn; if (!strnicmp(str, "std_", 4) || !strnicmp(str, "std+", 4)) @@ -2237,21 +2331,27 @@ int Key_StringToKeynum (const char *str, int *modifier) #if 0//def _WIN32 return VkKeyScan(str[0]); #else - return str[0]; + k = str[0]; +// return str[0]; #endif } - - if (!strncmp(str, "K_", 2)) - str+=2; - - for (kn=keynames ; kn->name ; kn++) + else { - if (!Q_strcasecmp(str,kn->name)) - return kn->keynum; + if (!strncmp(str, "K_", 2)) + str+=2; + + for (kn=keynames ; kn->name ; kn++) + { + if (!Q_strcasecmp(str,kn->name)) + return kn->keynum; + } + k = atoi(str); } - if (atoi(str)) //assume ascii code. (prepend with a 0 if needed) + if (k) //assume ascii code. (prepend with a 0 if needed) { - return atoi(str); + if (k >= 'A' && k <= 'Z') + k += 'a'-'A'; + return k; } return -1; } @@ -2548,14 +2648,28 @@ void Key_Bind_f (void) char *alias = Cmd_AliasExist(keybindings[b][modifier], RESTRICT_LOCAL); char quotedbind[2048]; char quotedalias[2048]; + char leveldesc[1024]; + if (bindcmdlevel[b][modifier] != level) + { + if (Cmd_ExecLevel > RESTRICT_SERVER) + Q_snprintfz(leveldesc, sizeof(leveldesc), ", for seat %i", Cmd_ExecLevel - RESTRICT_SERVER-1); + else if (Cmd_ExecLevel == RESTRICT_SERVER) + Q_snprintfz(leveldesc, sizeof(leveldesc), ", bound by server"); + else if (bindcmdlevel[b][modifier]>=RESTRICT_INSECURE) + Q_snprintfz(leveldesc, sizeof(leveldesc), ", bound by insecure source"); + else + Q_snprintfz(leveldesc, sizeof(leveldesc), ", at level %i", bindcmdlevel[b][modifier]); + } + else + *leveldesc = 0; COM_QuotedString(keybindings[b][modifier], quotedbind, sizeof(quotedbind), false); if (alias) { COM_QuotedString(alias, quotedalias, sizeof(quotedalias), false); - Con_Printf ("^[\"%s\"\\type\\bind %s %s^] = ^[\"%s\"\\type\\alias %s %s^]\n", Cmd_Argv(1), Cmd_Argv(1), quotedbind, keybindings[b][modifier], keybindings[b][modifier], quotedalias); + Con_Printf ("^[\"%s\"\\type\\bind %s %s^] = ^[\"%s\"\\type\\alias %s %s^]%s\n", Cmd_Argv(1), Cmd_Argv(1), quotedbind, keybindings[b][modifier], keybindings[b][modifier], quotedalias, leveldesc); } else - Con_Printf ("^[\"%s\"\\type\\bind %s %s^] = \"%s\"\n", Cmd_Argv(1), keybindings[b][modifier], Cmd_Argv(1), keybindings[b][modifier] ); + Con_Printf ("^[\"%s\"\\type\\bind %s %s^] = \"%s\"%s\n", Cmd_Argv(1), keybindings[b][modifier], Cmd_Argv(1), keybindings[b][modifier], leveldesc); } else Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); diff --git a/engine/client/keys.h b/engine/client/keys.h index 0dfb9d35..338286c3 100644 --- a/engine/client/keys.h +++ b/engine/client/keys.h @@ -311,6 +311,7 @@ qboolean Key_GetConsoleSelectionBox(struct console_s *con, int *sx, int *sy, int qboolean Key_MouseShouldBeFree(void); const char *Key_Demoji(char *buffer, size_t buffersize, const char *in); //strips out :smile: stuff. +void Key_EmojiCompletion_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx); #endif #endif diff --git a/engine/client/m_download.c b/engine/client/m_download.c index cf2b1150..b7fa1bf4 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -13,32 +13,7 @@ #ifdef PACKAGEMANAGER #include "fs.h" -//whole load of extra args for the downloads menu (for the downloads menu to handle engine updates). -#if defined(VKQUAKE) && !defined(SERVERONLY) -#define PHPVK "&vk=1" -#else -#define PHPVK -#endif -#if defined(GLQUAKE) && !defined(SERVERONLY) -#define PHPGL "&gl=1" -#else -#define PHPGL -#endif -#if defined(D3DQUAKE) && !defined(SERVERONLY) -#define PHPD3D "&d3d=1" -#else -#define PHPD3D -#endif -#ifdef MINIMAL -#define PHPMIN "&min=1" -#else -#define PHPMIN -#endif -#ifdef HAVE_LEGACY -#define PHPLEG "&leg=1&test=1" -#else -#define PHPLEG "&leg=0&test=1" -#endif +//some extra args for the downloads menu (for the downloads menu to handle engine updates). #if defined(_DEBUG) || defined(DEBUG) #define PHPDBG "&dbg=1" #else @@ -48,11 +23,10 @@ #define SVNREVISION - #endif #define SVNREVISIONSTR STRINGIFY(SVNREVISION) -#define DOWNLOADABLESARGS "ver=" SVNREVISIONSTR PHPVK PHPGL PHPD3D PHPMIN PHPLEG PHPDBG "&arch="PLATFORM "_" ARCH_CPU_POSTFIX +#define DOWNLOADABLESARGS PHPDBG -extern cvar_t pm_autoupdate; extern cvar_t pm_downloads_url; #define INSTALLEDFILES "installed.lst" //the file that resides in the quakedir (saying what's installed). @@ -194,10 +168,9 @@ static char *manifestpackages; //metapackage named by the manicfest. static char *declinedpackages; //metapackage named by the manicfest. static int domanifestinstall; //SECURITY_MANIFEST_* -#ifdef PLUGINS static qboolean pluginpromptshown; //so we only show prompts for new externally-installed plugins once, instead of every time the file is reloaded. -#endif #ifdef WEBCLIENT +static int allowphonehome = -1; //if autoupdates are disabled, make sure we get (temporary) permission before phoning home for available updates. (-1=unknown, 0=no, 1=yes) static qboolean doautoupdate; //updates will be marked (but not applied without the user's actions) static qboolean pkg_updating; //when flagged, further changes are blocked until completion. #else @@ -1221,6 +1194,20 @@ void PM_PluginDetected(void *ctx, int status) #endif #endif +#ifndef SERVERONLY +void PM_AutoUpdateQuery(void *ctx, int status) +{ + if (status == -1) + return; //'Later' + if (status == 1) + Cvar_ForceSet(&pkg_autoupdate, "0"); //'Disable' + else + Cvar_ForceSet(&pkg_autoupdate, "1"); //'Enable' + PM_WriteInstalledPackages(); + Menu_Download_Update(); +} +#endif + static void PM_PreparePackageList(void) { //figure out what we've previously installed. @@ -1251,6 +1238,13 @@ static void PM_PreparePackageList(void) } } #endif + if (!pluginpromptshown && pkg_autoupdate.ival < 0 && numdownloadablelists) + { + pluginpromptshown = true; +#ifndef SERVERONLY + Menu_Prompt(PM_AutoUpdateQuery, NULL, "Would you like to\nenable update checks?", "Enable", "Disable", "Later"); +#endif + } } } @@ -1821,6 +1815,22 @@ static void PM_ListDownloaded(struct dl_download *dl) } } #endif +#ifndef SERVERONLY +static void PM_UpdatePackageList(qboolean autoupdate, int retry); +static void PM_AllowPackageListQuery_Callback(void *ctx, int opt) +{ + unsigned int i; + allowphonehome = (opt==0); + + //something changed, let it download now. + for (i = 0; i < numdownloadablelists; i++) + { + if (downloadablelist[i].received == -2) + downloadablelist[i].received = 0; + } + PM_UpdatePackageList(false, 0); +} +#endif //retry 1== static void PM_UpdatePackageList(qboolean autoupdate, int retry) { @@ -1841,15 +1851,32 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry) #else doautoupdate |= autoupdate; +#ifdef SERVERONLY + allowphonehome = true; //erk. +#else + if (pkg_autoupdate.ival >= 1) + allowphonehome = true; + else if (allowphonehome == -1) + { + Menu_Prompt(PM_AllowPackageListQuery_Callback, NULL, "Query updates list?\n", "Okay", NULL, "Nope"); + return; + } +#endif + //kick off the initial tier of list-downloads. for (i = 0; i < numdownloadablelists; i++) { - if (downloadablelist[i].received) + if (downloadablelist[i].received && allowphonehome>=0) continue; autoupdate = false; if (downloadablelist[i].curdl) continue; + if (allowphonehome<=0) + { + downloadablelist[i].received = -2; + continue; + } downloadablelist[i].curdl = HTTP_CL_Get(va("%s%s"DOWNLOADABLESARGS, downloadablelist[i].url, strchr(downloadablelist[i].url,'?')?"&":"?"), NULL, PM_ListDownloaded); if (downloadablelist[i].curdl) { @@ -1884,7 +1911,6 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry) - static void COM_QuotedConcat(const char *cat, char *buf, size_t bufsize) { const unsigned char *gah; @@ -3579,7 +3605,7 @@ static int MD_AddItemsToDownloadMenu(emenu_t *m, int y, const char *pathprefix) if (!mo) { y += 8; - MC_AddBufferedText(m, 48, 320, y, path+prefixlen, false, true); + MC_AddBufferedText(m, 48, 320-16, y, path+prefixlen, false, true); y += 8; Q_strncatz(path, "/", sizeof(path)); y = MD_AddItemsToDownloadMenu(m, y, path); @@ -3690,20 +3716,20 @@ static void MD_Download_UpdateStatus(struct emenu_s *m) info->populated = true; MC_AddFrameStart(m, 48); y = 48; - b = MC_AddCommand(m, 48, 170, y, "Apply", MD_ApplyDownloads); + b = MC_AddCommand(m, 48, 320-16, y, "Apply", MD_ApplyDownloads); b->rightalign = false; b->common.tooltip = "Enable/Disable/Download/Delete packages to match any changes made (you will be prompted with a list of the changes that will be made)."; y+=8; - d = b = MC_AddCommand(m, 48, 170, y, "Back", MD_PopMenu); + d = b = MC_AddCommand(m, 48, 320-16, y, "Back", MD_PopMenu); b->rightalign = false; y+=8; #ifdef WEBCLIENT - b = MC_AddCommand(m, 48, 170, y, "Mark Updates", MD_MarkUpdatesButton); + b = MC_AddCommand(m, 48, 320-16, y, "Mark Updates", MD_MarkUpdatesButton); b->rightalign = false; b->common.tooltip = "Select any updated versions of packages that are already installed."; y+=8; #endif - b = MC_AddCommand(m, 48, 170, y, "Revert Updates", MD_RevertUpdates); + b = MC_AddCommand(m, 48, 320-16, y, "Revert Updates", MD_RevertUpdates); b->rightalign = false; b->common.tooltip = "Reset selection to only those packages that are currently installed."; y+=8; @@ -3711,7 +3737,7 @@ static void MD_Download_UpdateStatus(struct emenu_s *m) c = MC_AddCustom(m, 48, y, p, 0, NULL); c->draw = MD_AutoUpdate_Draw; c->key = MD_AutoUpdate_Key; - c->common.width = 320; + c->common.width = 320-48-16; c->common.height = 8; y += 8; #endif @@ -3761,7 +3787,7 @@ void Menu_DownloadStuff_f (void) //should only be called AFTER the filesystem etc is inited. void Menu_Download_Update(void) { - if (!pkg_autoupdate.ival) + if (pkg_autoupdate.ival <= 0) return; PM_UpdatePackageList(true, 2); diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 06144d43..e02c0cf2 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -176,7 +176,7 @@ float media_fadeouttime; sfx_t *Media_NextTrack(int musicchannelnum, float *starttime) { sfx_t *s = NULL; - if (bgmvolume.value <= 0) + if (bgmvolume.value <= 0 || mastervolume.value <= 0) return NULL; if (media_fadeout) @@ -2816,6 +2816,10 @@ void Media_SetState(cin_t *cin, cinstates_t newstate) } cinstates_t Media_GetState(cin_t *cin) { + if (!cin) + cin = R_ShaderGetCinematic(videoshader); + if (!cin) + return CINSTATE_INVALID; return cin->playstate; } diff --git a/engine/client/m_options.c b/engine/client/m_options.c index f784c326..0891c1eb 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -75,32 +75,75 @@ enum ASPECT2D_CUSTOM, }; + +typedef struct { + unsigned int w, h; +} ftevidmode_t; +static ftevidmode_t *vidmodes; +static size_t nummodes; +void VidMode_Clear(void) +{ + BZ_Free(vidmodes); + vidmodes = NULL; + nummodes = 0; +} +void VidMode_Add(int w, int h) +{ + size_t m; + for (m = 0; m < nummodes; m++) + { + if (vidmodes[m].w == w && vidmodes[m].h == h) + return; + } + Z_ReallocElements((void**)&vidmodes, &nummodes, nummodes+1, sizeof(*vidmodes)); + vidmodes[m].w = w; + vidmodes[m].h = h; +} +static int QDECL VidMode_Sort(const void *v1, const void *v2) +{ + const ftevidmode_t *m1 = v1, *m2 = v2; + int n1 = m1->w, n2=m2->w; //sort by width + if (n1 == n2) + n1 = m1->h, n2=m2->h; //then by height + if (n1 == n2) + return 0; //then give up and consider equal... which shouldn't happen. + if (n1 > n2) + return 1; + return -1; +} qboolean M_Vid_GetMode(qboolean forfullscreen, int num, int *w, int *h) { - int i; - - for (i = 0; i < 4; i++) + int a; + extern cvar_t vid_devicename; + if (!nummodes) { - const char **v = resaspects[i]; - while (*v && num) + VidMode_Clear(); + + if (rf->VID_EnumerateVideoModes) + rf->VID_EnumerateVideoModes("", vid_devicename.string, VidMode_Add); + + if (!nummodes) { - v++; - num--; - } - if (*v) - { - const char *c = *v; - const char *s = strchr(c, 'x'); - if (s) + for (a = 0; a < countof(resaspects); a++) { - *w = atoi(c); - *h = atoi(s + 1); - return true; + const char **v = resaspects[a]; + for (; *v; v++) + { + const char *c = *v; + const char *s = strchr(c, 'x'); + if (s) + VidMode_Add(atoi(c), atoi(s+1)); + } } - return false; } + qsort(vidmodes, nummodes, sizeof(*vidmodes), VidMode_Sort); } - return false; + + if (num < 0 || num >= nummodes) + return false; + *w = vidmodes[num].w; + *h = vidmodes[num].h; + return true; } @@ -2616,7 +2659,8 @@ void CheckCustomMode(struct emenu_s *menu) info->res2dsize[sel]->common.ishidden = false; } -int M_MatchModes(int width, int height, int *outres) +//return value is aspect group, *outres is the mode index inside that aspect. +static int M_MatchModes(int width, int height, int *outres) { int i; int ratio = -1; @@ -3332,7 +3376,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu ent.framestate.g[FS_REG].frame[0] = mods->framegroup; ent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = realtime - mods->framechangetime; ent.framestate.g[FS_REG].endbone = 0x7fffffff; - ent.customskin = Mod_RegisterSkinFile(va("%s_0.skin", mods->modelname)); + ent.customskin = Mod_RegisterSkinFile(va("%s_%i.skin", mods->modelname, ent.skinnum)); ent.light_avg[0] = ent.light_avg[1] = ent.light_avg[2] = 0.66; ent.light_range[0] = ent.light_range[1] = ent.light_range[2] = 0.33; diff --git a/engine/client/menu.c b/engine/client/menu.c index 6ec8b1ca..48c0d976 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -1105,11 +1105,12 @@ static char *quitMessage [] = "Press N to stay proud\n" "and successful!", - "If you press Y to\n" + //this is a vanilla message... but I'm having enough issues with false-positives in malware scanners right now that I really don't want to risk an already-suspicious actual hunam seeing stuff like this... :) +/* "If you press Y to\n" "quit, I will summon\n" "Satan all over your\n" "hard drive!", - +*/ "Um, Asmodeus dislikes\n" "his children trying to\n" "quit. Press Y to return\n" diff --git a/engine/client/menu.h b/engine/client/menu.h index e9cda8bf..eca73d2e 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -497,6 +497,8 @@ void MP_RegisterCvarsAndCmds(void); int MP_BuiltinValid(const char *name, int num); qboolean MP_ConsoleCommand(const char *cmdtext); int MP_GetServerCategory(int index); +#else +#define MP_UsingGamecodeLoadingScreen() false #endif #ifdef MENU_NATIVECODE @@ -535,6 +537,7 @@ void Plug_SBar(playerview_t *pv); qboolean Plug_ServerMessage(char *buffer, int messagelevel); void Plug_Tick(void); qboolean Plugin_ExecuteString(void); +void Plug_FreeAllImages(void); #ifdef ANDROID #define PLUGINPREFIX "libplug_" //android is kinda annoying and only extracts specific files. diff --git a/engine/client/merged.h b/engine/client/merged.h index 24389f96..da68fd4c 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -131,7 +131,7 @@ extern void (*VID_DeInit) (void); extern char *(*VID_GetRGBInfo) (int *stride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt); //if stride is negative, then the return value points to the last line intead of the first. this allows it to be freed normally. extern void (*VID_SetWindowCaption) (const char *msg); -extern void *SCR_ScreenShot_Capture (int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt, qboolean no2d); +extern void *SCR_ScreenShot_Capture (int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt, qboolean no2d, qboolean hdr); extern void SCR_Init (void); extern void SCR_DeInit (void); extern qboolean (*SCR_UpdateScreen) (void); @@ -479,6 +479,7 @@ typedef struct rendererinfo_s { //FIXME: keep this... int (*VID_GetPriority) (void); //so that eg x11 or wayland can be prioritised depending on environment settings. assumed to be 1. + void (*VID_EnumerateVideoModes) (const char *driver, const char *output, void (*cb) (int w, int h)); //FIXME: add getdestopres //FIXME: add clipboard handling diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 3130f855..0f9cf45b 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -505,6 +505,9 @@ void SV_Master_Heartbeat (void) if (realtime-interval - svs.last_heartbeat < interval) return; // not time to send yet + if ((sv.allocated_client_slots == 1) && !isDedicated) + return; //don't heartbeat in single-player, we don't even have a public socket open! + svs.last_heartbeat = realtime-interval; svs.heartbeat_sequence++; diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 18be45dc..4345fd58 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -169,7 +169,7 @@ typedef struct skytriblock_s //this is the required render state for each particle //dynamic per-particle stuff isn't important. only static state. typedef struct { - enum {PT_NORMAL, PT_SPARK, PT_SPARKFAN, PT_TEXTUREDSPARK, PT_BEAM, PT_CDECAL, PT_UDECAL, PT_INVISIBLE} type; + enum {PT_NORMAL, PT_SPARK, PT_SPARKFAN, PT_TEXTUREDSPARK, PT_BEAM, PT_VBEAM, PT_CDECAL, PT_UDECAL, PT_INVISIBLE} type; blendmode_t blendmode; shader_t *shader; @@ -948,6 +948,12 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) ptype->looks.shader = R_RegisterShader(va("beam%s", namepostfix), SUF_NONE, defaultshader); TEXASSIGNF(tn.base, beamtexture); } + else if (ptype->looks.type == PT_VBEAM) + { + /*untextured beams get a single continuous blob*/ + ptype->looks.shader = R_RegisterShader(va("vbeam%s", namepostfix), SUF_NONE, defaultshader); + TEXASSIGNF(tn.base, beamtexture); + } else if (ptype->looks.type == PT_SPARKFAN) { /*untextured beams get a single continuous blob*/ @@ -1942,6 +1948,8 @@ parsefluid: { if (!strcmp(value, "beam")) ptype->looks.type = PT_BEAM; + else if (!strcmp(value, "vbeam")) + ptype->looks.type = PT_BEAM; else if (!strcmp(value, "spark") || !strcmp(value, "linespark")) ptype->looks.type = PT_SPARK; else if (!strcmp(value, "sparkfan") || !strcmp(value, "trianglefan")) @@ -2279,7 +2287,7 @@ parsefluid: FinishParticleType(ptype); - if (ptype->looks.type == PT_BEAM && !setbeamlen) + if ((ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) && !setbeamlen) ptype->rotationstartmin = 1/128.0; } @@ -2324,6 +2332,9 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) case PT_BEAM: Q_strncatz(outstr, "type beam\n", outstrlen); break; + case PT_VBEAM: + Q_strncatz(outstr, "type vbeam\n", outstrlen); + break; case PT_CDECAL: if (ptype->surfflagmatch || ptype->surfflagmask) Q_strncatz(outstr, va("clippeddecal %#x %#x\n", ptype->surfflagmask, ptype->surfflagmatch), outstrlen); @@ -2792,7 +2803,7 @@ static void FinishParticleType(part_type_t *ptype) ptype->looks.type = PT_SPARK; if (ptype->looks.type == PT_SPARK && !r_part_sparks.ival) ptype->looks.type = PT_INVISIBLE; - if (ptype->looks.type == PT_BEAM && r_part_beams.ival <= 0) + if ((ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) && r_part_beams.ival <= 0) ptype->looks.type = PT_INVISIBLE; if (ptype->rampmode && !ptype->ramp) @@ -2836,6 +2847,19 @@ static void FinishEffectinfoParticleType(part_type_t *ptype, qboolean blooddecal ptype->scaledelta *= 2*2*ptype->looks.stretch; //fixme: this feels wrong, the results look correct though. hrmph. ptype->looks.stretch = 1; } + else if (ptype->looks.type == PT_BEAM || ptype->looks.type == PT_VBEAM) + { //ignore what the particle says and just spawn it from a to b. don't accept mid-segment randomness. + if (ptype->t1 == 0 && ptype->t2 == 1) + ptype->looks.type = PT_VBEAM; + else + ptype->looks.type = PT_BEAM; + ptype->count = 0; + ptype->countextra = 2; + ptype->countrand = 0; + ptype->countspacing = 10000; + ptype->scale *= 0.5; + } + if (blooddecalonimpact) //DP blood particles generate decals unconditionally (and prevent blood from bouncing) ptype->clipbounce = -2; if (ptype->looks.type == PT_TEXTUREDSPARK) @@ -2864,10 +2888,15 @@ static void P_ImportEffectInfo(char *config, char *line) part_type_t *ptype = NULL; int parenttype; char arg[8][1024]; - int args = 0; + int args; qboolean blooddecalonimpact = false; //tracked separately because it needs to override another field - float teximages[256][4]; + + struct + { + float tc[4]; + char imgname[128]; + } teximages[256]; { int i; @@ -2876,37 +2905,64 @@ static void P_ImportEffectInfo(char *config, char *line) //default assumes 8*8 grid, but we allow more for (i = 0; i < 256; i++) { - teximages[i][0] = 1/8.0 * (i & 7); - teximages[i][1] = 1/8.0 * (1+(i & 7)); - teximages[i][2] = 1/8.0 * (1+(i>>3)); - teximages[i][3] = 1/8.0 * (i>>3); + teximages[i].tc[0] = 1/8.0 * (i & 7); + teximages[i].tc[1] = 1/8.0 * (1+(i & 7)); + teximages[i].tc[2] = 1/8.0 * (1+(i>>3)); + teximages[i].tc[3] = 1/8.0 * (i>>3); + strcpy(teximages[i].imgname, "particles/particlefont"); } + //and this one needs to be subject to a hack. ho hum. + teximages[60].tc[0] = 0; + teximages[60].tc[1] = 1; + teximages[60].tc[2] = 0; + teximages[60].tc[3] = 1; + strcpy(teximages[60].imgname, "particles/nexbeam"); + file = FS_OpenVFS("particles/particlefont.txt", "rb", FS_GAME); if (file) { while (VFS_GETS(file, linebuf, sizeof(linebuf))) { - line = COM_StringParse(linebuf, arg[0], sizeof(arg[0]), false, false); - line = COM_StringParse(line, arg[1], sizeof(arg[1]), false, false); - line = COM_StringParse(line, arg[2], sizeof(arg[2]), false, false); - line = COM_StringParse(line, arg[3], sizeof(arg[3]), false, false); - line = COM_StringParse(line, arg[4], sizeof(arg[4]), false, false); - + args = 0; + line = COM_StringParse(linebuf, arg[args], sizeof(arg[args]), false, false); if (line) { - i = atoi(arg[0]); - teximages[i][0] = atof(arg[1]); - teximages[i][1] = atof(arg[3]); - teximages[i][2] = atof(arg[4]); - teximages[i][3] = atof(arg[2]); + for (args++; args < countof(arg); args++) + { + line = COM_StringParse(line, arg[args], sizeof(arg[args]), false, false); + if (!line) + break; + } } + + i = atoi(arg[0]); + if (i >= countof(teximages)) + Con_Printf("particles/particlefont.txt: index too high - %i>=%u\n", i, (unsigned)countof(teximages)); + else if (args == 2) + { + teximages[i].tc[0] = 0; + teximages[i].tc[1] = 1; + teximages[i].tc[2] = 0; + teximages[i].tc[3] = 1; + Q_strncpyz(teximages[i].imgname, arg[1], sizeof(teximages[i].imgname)); + } + else if (args >= 5 && args <= 6) + { + teximages[i].tc[0] = atof(arg[1]); //s1 + teximages[i].tc[1] = atof(arg[3]); //s2 + teximages[i].tc[2] = atof(arg[4]); //t1 + teximages[i].tc[3] = atof(arg[2]); //t2 + Q_strncpyz(teximages[i].imgname, (args>=6)?arg[5]:"particles/particlefont", sizeof(teximages[i].imgname)); + } + else + Con_Printf("particles/particlefont.txt: unsupported argument count - %i\n", args); } VFS_CLOSE(file); } } - for (;;) + for (args = 0;;) { if (!*line) break; @@ -2964,7 +3020,6 @@ static void P_ImportEffectInfo(char *config, char *line) ptype->alpharand = 1; ptype->alphachange = -1; ptype->die = 9999; - strcpy(ptype->texname, "particles/particlefont"); ptype->rgb[0] = 1; ptype->rgb[1] = 1; ptype->rgb[2] = 1; @@ -2983,10 +3038,11 @@ static void P_ImportEffectInfo(char *config, char *line) ptype->dl_time = 0; i = 63; //default texture is 63. - ptype->s1 = teximages[i][0]; - ptype->s2 = teximages[i][1]; - ptype->t1 = teximages[i][2]; - ptype->t2 = teximages[i][3]; + Q_strncpyz(ptype->texname, teximages[i].imgname, sizeof(ptype->texname)); + ptype->s1 = teximages[i].tc[0]; + ptype->s2 = teximages[i].tc[1]; + ptype->t1 = teximages[i].tc[2]; + ptype->t2 = teximages[i].tc[3]; ptype->texsstride = 0; ptype->randsmax = 1; } @@ -3064,20 +3120,28 @@ static void P_ImportEffectInfo(char *config, char *line) ptype->looks.premul = 2; ptype->flurry = 32; //may not still be valid later, but at least it would be an obvious issue with the original. } + else if (!strcmp(arg[1], "entityparticle")) + { + ptype->die = 0; + ptype->looks.type = PT_NORMAL; + ptype->looks.blendmode = BM_PREMUL;//BM_BLEND; + ptype->looks.premul = 1; + } else { Con_Printf("effectinfo type %s not supported\n", arg[1]); } } else if (!strcmp(arg[0], "tex") && args == 3) - { + { //assume that all textures will be packed into the same texture and on the same line... int mini = atoi(arg[1]); int maxi = atoi(arg[2]); - ptype->s1 = teximages[mini][0]; - ptype->s2 = teximages[mini][1]; - ptype->t1 = teximages[mini][2]; - ptype->t2 = teximages[mini][3]; - ptype->texsstride = teximages[(mini+1)&(sizeof(teximages)/sizeof(teximages[0])-1)][0] - teximages[mini][0]; + Q_strncpyz(ptype->texname, teximages[mini].imgname, sizeof(ptype->texname)); + ptype->s1 = teximages[mini].tc[0]; + ptype->s2 = teximages[mini].tc[1]; + ptype->t1 = teximages[mini].tc[2]; + ptype->t2 = teximages[mini].tc[3]; + ptype->texsstride = teximages[(mini+1)&(sizeof(teximages)/sizeof(teximages[0])-1)].tc[0] - teximages[mini].tc[0]; ptype->randsmax = (maxi - mini); if (ptype->randsmax < 1) ptype->randsmax = 1; @@ -4137,7 +4201,7 @@ static void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t a { case SM_UNICIRCLE: m = pmax; - if (ptype->looks.type == PT_BEAM) + if (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) m--; if (m < 1) @@ -4769,7 +4833,7 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, { case SM_UNICIRCLE: m = pcount; - if (ptype->looks.type == PT_BEAM) + if (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) m--; if (m < 1) @@ -4843,7 +4907,7 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, if (!free_particles) break; p = free_particles; - if (ptype->looks.type == PT_BEAM) + if (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) { if (!free_beams) break; @@ -5137,7 +5201,7 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, } // update beam list - if (ptype->looks.type == PT_BEAM) + if (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) { if (b) { @@ -5672,7 +5736,7 @@ static void P_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptyp } p = free_particles; - if (ptype->looks.type == PT_BEAM) + if (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) { if (!free_beams) { @@ -5950,7 +6014,7 @@ static void P_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptyp ts->state1.lastdist = len; // update beamseg list - if (ptype->looks.type == PT_BEAM) + if (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) { if (b) { @@ -5986,7 +6050,7 @@ static void P_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptyp } } } - else if (ptype->looks.type == PT_BEAM) + else if (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) { if (b) { @@ -6423,6 +6487,73 @@ static void GL_DrawTexturedSparkParticle(int count, particle_t **plist, plooks_t } } + +static void GL_DrawParticleVBeam(int count, beamseg_t **blist, plooks_t *type) +{ + beamseg_t *b; + vec3_t v; + vec3_t cr; + beamseg_t *c; + particle_t *p; + particle_t *q; + float ts; + + while(count--) + { + b = *blist++; + + if (pscriptmesh.numvertexes >= BUFFERVERTS-4) + { + pscriptmesh.numindexes = pscriptmesh.numvertexes/4*6; + BE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0); + pscriptmesh.numvertexes = 0; + } + + c = b->next; + + q = c->p; + if (!q) + continue; + p = b->p; + +// q->rgba[3] = 1; +// p->rgba[3] = 1; + + VectorSubtract(r_refdef.vieworg, q->org, v); + VectorNormalize(v); + CrossProduct(c->dir, v, cr); + VectorNormalize(cr); + ts = c->texture_s*q->angle + particletime*q->rotationspeed; + Vector4Copy(q->rgba, pscriptcolours[pscriptmesh.numvertexes+0]); + Vector4Copy(q->rgba, pscriptcolours[pscriptmesh.numvertexes+1]); + Vector2Set(pscripttexcoords[pscriptmesh.numvertexes+0], p->s1, p->t1); + Vector2Set(pscripttexcoords[pscriptmesh.numvertexes+1], p->s2, p->t2); + VectorMA(q->org, -q->scale, cr, pscriptverts[pscriptmesh.numvertexes+0]); + VectorMA(q->org, q->scale, cr, pscriptverts[pscriptmesh.numvertexes+1]); + + VectorSubtract(r_refdef.vieworg, p->org, v); + VectorNormalize(v); + CrossProduct(b->dir, v, cr); // replace with old p->dir? + VectorNormalize(cr); + ts = b->texture_s*p->angle + particletime*p->rotationspeed; + (void)ts; + Vector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+2]); + Vector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+3]); + Vector2Set(pscripttexcoords[pscriptmesh.numvertexes+2], p->s1, p->t1); + Vector2Set(pscripttexcoords[pscriptmesh.numvertexes+3], p->s2, p->t2); + VectorMA(p->org, p->scale, cr, pscriptverts[pscriptmesh.numvertexes+2]); + VectorMA(p->org, -p->scale, cr, pscriptverts[pscriptmesh.numvertexes+3]); + + pscriptmesh.numvertexes += 4; + } + + if (pscriptmesh.numvertexes) + { + pscriptmesh.numindexes = pscriptmesh.numvertexes/4*6; + BE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0); + pscriptmesh.numvertexes = 0; + } +} static void GL_DrawParticleBeam(int count, beamseg_t **blist, plooks_t *type) { beamseg_t *b; @@ -6982,6 +7113,9 @@ static void PScript_DrawParticleTypes (void) case PT_BEAM: bdraw = GL_DrawParticleBeam; break; + case PT_VBEAM: + bdraw = GL_DrawParticleVBeam; + break; case PT_CDECAL: break; case PT_UDECAL: diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 8def4744..289d276b 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -461,6 +461,19 @@ void QCBUILTIN PF_cl_setwindowcaption(pubprogfuncs_t *prinst, struct globalvars_ } } +void QCBUILTIN PF_cl_setmousepos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + world_t *world = prinst->parms->user; + float *pos = G_VECTOR(OFS_PARM0); + + if (key_dest_absolutemouse & world->keydestmask) + { + vid.forcecursor = true; + vid.forcecursorpos[0] = (pos[0] * vid.pixelwidth) / vid.width; + vid.forcecursorpos[1] = (pos[1] * vid.pixelheight) / vid.height; + } +} + //#343 void QCBUILTIN PF_cl_setcursormode (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -654,12 +667,17 @@ void QCBUILTIN PF_cs_media_getproperty (pubprogfuncs_t *prinst, struct globalvar } void QCBUILTIN PF_cs_media_getstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - const char *shader = PR_GetStringOfs(prinst, OFS_PARM0); cinstates_t ret = CINSTATE_INVALID; - cin_t *cin; - cin = R_ShaderFindCinematic(shader); - if (cin) - ret = Media_GetState(cin); + if (prinst->callargc>0) + { + const char *shader = PR_GetStringOfs(prinst, OFS_PARM0); + cin_t *cin; + cin = R_ShaderFindCinematic(shader); + if (cin) + ret = Media_GetState(cin); + } + else + ret = Media_GetState(NULL); G_FLOAT(OFS_RETURN) = ret; } @@ -911,8 +929,8 @@ void QCBUILTIN PF_cl_SetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr { int bm[2] = { - G_FLOAT(OFS_PARM0+0), - G_FLOAT(OFS_PARM0+1) + G_VECTOR(OFS_PARM0)[0], + G_VECTOR(OFS_PARM0)[1] }; Key_SetBindMap(bm); G_FLOAT(OFS_RETURN) = 1; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 8fd00290..fc5c67dd 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -80,6 +80,7 @@ static char csqc_printbuffer[8192]; cvar_t pr_csqc_maxedicts = CVAR("pr_csqc_maxedicts", "65536"); //not tied to protocol nor server. can be set arbitrarily high, except for memory allocations. cvar_t pr_csqc_memsize = CVAR("pr_csqc_memsize", "-1"); cvar_t cl_csqcdebug = CVAR("cl_csqcdebug", "0"); //prints entity numbers which arrive (so I can tell people not to apply it to players...) +static cvar_t cl_csqc_nodeprecate = CVARAD("cl_csqc_nodeprecate", "0", "dpcompat_nocsqcwarnings", "When set, disables deprecation warnings."); cvar_t cl_nocsqc = CVAR("cl_nocsqc", "0"); cvar_t pr_csqc_coreonerror = CVAR("pr_csqc_coreonerror", "1"); #if defined(NOBUILTINMENUS) && !defined(MENU_DAT) @@ -87,6 +88,7 @@ cvar_t pr_csqc_formenus = CVARF("pr_csqc_formenus", "1", CVAR_NOSET); #else cvar_t pr_csqc_formenus = CVAR("pr_csqc_formenus", "0"); #endif +static cvar_t dpcompat_csqcinputeventtypes = CVARD("dpcompat_csqcinputeventtypes", "999999", "Specifies the first csqc input event that the mod does not recognise. This should never have been a thing, but some mods are simply too buggy."); extern cvar_t dpcompat_stats; // standard effect cvars/sounds @@ -743,13 +745,13 @@ static void QCBUILTIN PF_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_g if (!strcmp(str, "vid_conwidth")) { if (!csqc_isdarkplaces) //don't warn when its unfixable... - csqc_deprecated("vid_conwidth cvar has aspect issues"); + csqc_deprecated("vid_conwidth - use (vector)getviewprop(VF_SCREENVSIZE)"); G_FLOAT(OFS_RETURN) = vid.width; } else if (!strcmp(str, "vid_conheight")) { if (!csqc_isdarkplaces) - csqc_deprecated("vid_conheight cvar has aspect issues"); + csqc_deprecated("vid_conheight - use (vector)getviewprop(VF_SCREENVSIZE)"); G_FLOAT(OFS_RETURN) = vid.height; } else @@ -799,7 +801,7 @@ static void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars char *funcname = NULL; int args; int builtinno; - if (prinst->GetFunctionInfo(prinst, funcref, &args, &builtinno, funcname, sizeof(funcname))) + if (prinst->GetFunctionInfo(prinst, funcref, &args, NULL, &builtinno, funcname, sizeof(funcname))) { //qc defines the function at least. nothing weird there... if (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins) { @@ -954,7 +956,9 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *fte_restrict in, entity_t *ft csqcedict_t *p = (csqcedict_t*)skel_gettaginfo_args(csqcprogs, out->axis, out->origin, in->xv->tag_entity, in->xv->tag_index); if (p && (int)p->xv->renderflags & CSQCRF_VIEWMODEL) out->flags |= RF_DEPTHHACK|RF_WEAPONMODEL; + out->pvscache = p->pvsinfo; //for the areas. out->pvscache.num_leafs = -1; //make visible globally + out->pvscache.headnode = 0; #endif } @@ -1706,6 +1710,117 @@ void QCBUILTIN PF_R_PolygonEnd(pubprogfuncs_t *prinst, struct globalvars_s *pr_g csqc_poly_startidx = cl_numstrisidx; } +//input is a line of verts, output is a quad strip +void QCBUILTIN PF_R_PolygonEndRibbon(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + int i; + int nv; + int flags = csqc_poly_flags; + int first; + + vec3_t tang; + vec3_t dir; + vec3_t eyedir; + + float separation = G_FLOAT(OFS_PARM0); + float sseparation = G_FLOAT(OFS_PARM1+0); + float tseparation = G_FLOAT(OFS_PARM1+1); + + if (!csqc_poly_shader) + return; + + nv = cl_numstrisvert-csqc_poly_startvert; + if (nv < 2) + { + csqc_poly_startvert = cl_numstrisvert; + csqc_poly_startidx = cl_numstrisidx; + return; //sod off. + } + flags &= ~BEF_LINES; + + if (flags != csqc_poly_flags || (cl_numstrisvert-csqc_poly_origvert) >= 32768) + { + int sv = cl_numstrisvert - nv; + cl_numstrisvert -= nv; + CSQC_PolyFlush(); + + csqc_poly_origvert = cl_numstrisvert; + csqc_poly_origidx = cl_numstrisidx; + R2D_Flush = CSQC_PolyFlush; + csqc_poly_flags = flags; + csqc_poly_startvert = cl_numstrisvert; + csqc_poly_startidx = cl_numstrisidx; + + memcpy(cl_strisvertv+cl_numstrisvert, cl_strisvertv + sv, sizeof(*cl_strisvertv) * nv); + memcpy(cl_strisvertt+cl_numstrisvert, cl_strisvertt + sv, sizeof(*cl_strisvertt) * nv); + memcpy(cl_strisvertc+cl_numstrisvert, cl_strisvertc + sv, sizeof(*cl_strisvertc) * nv); + cl_numstrisvert += nv; + } + + nv = cl_numstrisvert-csqc_poly_startvert; + //dupe the verts + if (cl_numstrisvert+nv < cl_maxstrisvert) + cl_stris_ExpandVerts(cl_numstrisvert+nv); + memcpy(&cl_strisvertv[cl_numstrisvert], &cl_strisvertv[csqc_poly_startvert], sizeof(*cl_strisvertv)*nv); + memcpy(&cl_strisvertt[cl_numstrisvert], &cl_strisvertt[csqc_poly_startvert], sizeof(*cl_strisvertt)*nv); + memcpy(&cl_strisvertc[cl_numstrisvert], &cl_strisvertc[csqc_poly_startvert], sizeof(*cl_strisvertc)*nv); + + //apply separation + VectorSubtract(cl_strisvertv[csqc_poly_startvert+0+1], cl_strisvertv[csqc_poly_startvert+0], dir); + VectorSubtract(cl_strisvertv[csqc_poly_startvert+0], r_refdef.vieworg, eyedir); + VectorNormalize(dir); VectorNormalize(eyedir); + CrossProduct(dir, eyedir, tang); + VectorMA(cl_strisvertv[csqc_poly_startvert+0], separation, tang, cl_strisvertv[csqc_poly_startvert+0]); + VectorMA(cl_strisvertv[cl_numstrisvert+0], -separation, tang, cl_strisvertv[cl_numstrisvert+0]); + cl_strisvertt[csqc_poly_startvert+0][0] += sseparation; //and update the generated s coord. + cl_strisvertt[csqc_poly_startvert+0][1] += tseparation; //and update the generated t coord. + for (i = 1; i < nv-1; i++) + { //direction comes from its two neighbours, rather than itself and one of those neighbours + VectorSubtract(cl_strisvertv[csqc_poly_startvert+i+1], cl_strisvertv[csqc_poly_startvert+i-1], dir); + VectorSubtract(cl_strisvertv[csqc_poly_startvert+i], r_refdef.vieworg, eyedir); + VectorNormalize(dir); VectorNormalize(eyedir); + CrossProduct(dir, eyedir, tang); + VectorMA(cl_strisvertv[csqc_poly_startvert+i], separation, tang, cl_strisvertv[csqc_poly_startvert+i]); + VectorMA(cl_strisvertv[cl_numstrisvert+i], -separation, tang, cl_strisvertv[cl_numstrisvert+i]); + cl_strisvertt[csqc_poly_startvert+i][0] += sseparation; //and update the generated s coord. + cl_strisvertt[csqc_poly_startvert+i][1] += tseparation; //and update the generated t coord. + } + //don't wrap over + VectorSubtract(cl_strisvertv[csqc_poly_startvert+i], cl_strisvertv[csqc_poly_startvert+i-1], dir); + VectorSubtract(cl_strisvertv[csqc_poly_startvert+i], r_refdef.vieworg, eyedir); + VectorNormalize(dir); VectorNormalize(eyedir); + CrossProduct(dir, eyedir, tang); + VectorMA(cl_strisvertv[csqc_poly_startvert+i], separation, tang, cl_strisvertv[csqc_poly_startvert+i]); + VectorMA(cl_strisvertv[cl_numstrisvert+i], -separation, tang, cl_strisvertv[cl_numstrisvert+i]); + cl_strisvertt[csqc_poly_startvert+i][0] += sseparation; //and update the generated s coord. + cl_strisvertt[csqc_poly_startvert+i][1] += tseparation; //and update the generated t coord. + //verts are all set up right + cl_numstrisvert += nv; + + if (cl_numstrisidx+(nv-1)*6 > cl_maxstrisidx) + { + cl_maxstrisidx=cl_numstrisidx+(nv-1)*6 + 64; + cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); + } + + first = csqc_poly_startvert - csqc_poly_origvert; + /*build a double-triangle strip out of our lined verts*/ + for (i = 1; i < nv; i++) + { + cl_strisidx[cl_numstrisidx++] = first + i-1; + cl_strisidx[cl_numstrisidx++] = first + i; + cl_strisidx[cl_numstrisidx++] = first + i+nv; + + cl_strisidx[cl_numstrisidx++] = first + i+nv; + cl_strisidx[cl_numstrisidx++] = first + i-1+nv; + cl_strisidx[cl_numstrisidx++] = first + i-1; + } + + /*set up ready for the next poly*/ + csqc_poly_startvert = cl_numstrisvert; + csqc_poly_startidx = cl_numstrisidx; +} + typedef struct { vec3_t xyz; @@ -1819,6 +1934,7 @@ void QCBUILTIN PF_R_AddTrisoup_Simple(pubprogfuncs_t *prinst, struct globalvars_ qboolean csqc_rebuildmatricies; float csqc_proj_matrix[16]; float csqc_proj_matrix_inverse[16]; +float csqc_proj_frustum[2]; void V_ApplyAFov(playerview_t *pv); void buildmatricies(void) { @@ -1829,15 +1945,26 @@ void buildmatricies(void) V_ApplyAFov(csqc_playerview); - /*build view and projection matricies*/ - Matrix4x4_CM_ModelViewMatrix(modelview, r_refdef.viewangles, r_refdef.vieworg); - if (r_refdef.useperspective) - Matrix4x4_CM_Projection2(proj, r_refdef.fov_x, r_refdef.fov_y, 4); - else - Matrix4x4_CM_Orthographic(proj, -r_refdef.fov_x/2, r_refdef.fov_x/2, -r_refdef.fov_y/2, r_refdef.fov_y/2, r_refdef.mindist, r_refdef.maxdist>=1?r_refdef.maxdist:9999); + if (csqc_isdarkplaces) + { + /*doesn't bother to use the projection matrix. it isn't much of a transform.*/ + Matrix4x4_CM_ModelViewMatrix(csqc_proj_matrix, r_refdef.viewangles, r_refdef.vieworg); - /*build the vp matrix*/ - Matrix4_Multiply(proj, modelview, csqc_proj_matrix); + csqc_proj_frustum[0] = tan(r_refdef.fov_x * M_PI / 360.0); + csqc_proj_frustum[1] = tan(r_refdef.fov_y * M_PI / 360.0); + } + else + { + /*build view and projection matricies*/ + Matrix4x4_CM_ModelViewMatrix(modelview, r_refdef.viewangles, r_refdef.vieworg); + if (r_refdef.useperspective) + Matrix4x4_CM_Projection2(proj, r_refdef.fov_x, r_refdef.fov_y, 4); + else + Matrix4x4_CM_Orthographic(proj, -r_refdef.fov_x/2, r_refdef.fov_x/2, -r_refdef.fov_y/2, r_refdef.fov_y/2, r_refdef.mindist, r_refdef.maxdist>=1?r_refdef.maxdist:9999); + + /*build the vp matrix*/ + Matrix4_Multiply(proj, modelview, csqc_proj_matrix); + } /*build the unproject matrix (inverted vp matrix)*/ Matrix4_Invert(csqc_proj_matrix, csqc_proj_matrix_inverse); @@ -1871,21 +1998,26 @@ static void QCBUILTIN PF_cs_project (pubprogfuncs_t *prinst, struct globalvars_s tempv[1] /= tempv[3]; tempv[2] /= tempv[3]; - out[0] = (1+tempv[0])/2; - out[1] = 1-(1+tempv[1])/2; - out[2] = tempv[2]; - if (csqc_isdarkplaces) { /*sigh*/ - out[0] = out[0]*vid.width + r_refdef.vrect.x; - out[1] = out[1]*vid.height + r_refdef.vrect.y; + tempv[0] = -tempv[0]/tempv[2]/csqc_proj_frustum[0]; + tempv[1] = tempv[1]/tempv[2]/csqc_proj_frustum[1]; + out[0] = (1+tempv[0])/2; + out[1] = (1+tempv[1])/2; + out[2] = -tempv[2]; + + out[0] = out[0]*vid.width; + out[1] = out[1]*vid.height; } else { + out[0] = (1+tempv[0])/2; + out[1] = 1-(1+tempv[1])/2; + out[2] = tempv[2]; + out[0] = out[0]*r_refdef.vrect.width + r_refdef.vrect.x; out[1] = out[1]*r_refdef.vrect.height + r_refdef.vrect.y; } - if (tempv[3] < 0) out[2] *= -1; } @@ -1904,23 +2036,28 @@ static void QCBUILTIN PF_cs_unproject (pubprogfuncs_t *prinst, struct globalvars if (csqc_isdarkplaces) { /*sigh*/ - tx = ((tx-r_refdef.vrect.x)/vid.width); - ty = ((ty-r_refdef.vrect.y)/vid.height); + tx = ((tx)/vid.width); + ty = ((ty)/vid.height); + + //this is kinda screwy + v[2] = -in[2]; + v[0] = -(tx*2-1)*v[2]*csqc_proj_frustum[0]; + v[1] = (ty*2-1)*v[2]*csqc_proj_frustum[1]; } else { tx = ((tx-r_refdef.vrect.x)/r_refdef.vrect.width); ty = ((ty-r_refdef.vrect.y)/r_refdef.vrect.height); - } - ty = 1-ty; - v[0] = tx*2-1; - v[1] = ty*2-1; - v[2] = in[2]*2-1; //gl projection matrix scales -1 to 1 (unlike d3d, which is 0 to 1) - v[3] = 1; + ty = 1-ty; + v[0] = tx*2-1; + v[1] = ty*2-1; + v[2] = in[2]*2-1; //gl projection matrix scales -1 to 1 (unlike d3d, which is 0 to 1) - //don't use 1, because the far clip plane really is an infinite distance away. and that tends to result division by infinity. - if (v[2] >= 1) - v[2] = 0.999999; + //don't use 1, because the far clip plane really is an infinite distance away. and that tends to result division by infinity. + if (v[2] >= 1) + v[2] = 0.999999; + } + v[3] = 1; Matrix4x4_CM_Transform4(csqc_proj_matrix_inverse, v, tempv); @@ -2214,6 +2351,13 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ case VF_SKYROOM_CAMERA: r_refdef.skyroom_enabled = true; VectorCopy(p, r_refdef.skyroom_pos); + if (prinst->callargc >= 4) + { + VectorCopy(G_VECTOR(OFS_PARM2), r_refdef.skyroom_spin); + r_refdef.skyroom_spin[3] = G_FLOAT(OFS_PARM3); + } + else + Vector4Set(r_refdef.skyroom_spin, 0, 0, 0, 0); break; case VF_ORIGIN: @@ -5400,7 +5544,7 @@ static void QCBUILTIN PF_DeltaListen(pubprogfuncs_t *prinst, struct globalvars_s return; } - if (!prinst->GetFunctionInfo(prinst, func, NULL, NULL, NULL, 0)) + if (!prinst->GetFunctionInfo(prinst, func, NULL, NULL, NULL, NULL, 0)) { Con_Printf("PF_DeltaListen: Bad function index\n"); return; @@ -6447,6 +6591,7 @@ static struct { {"R_BeginPolygon", PF_R_PolygonBegin, 306}, // #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???) {"R_PolygonVertex", PF_R_PolygonVertex, 307}, // #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex (EXT_CSQC_???) {"R_EndPolygon", PF_R_PolygonEnd, 308}, // #308 void() R_EndPolygon (EXT_CSQC_???) + {"R_EndPolygonRibbon", PF_R_PolygonEndRibbon, 0}, {"addtrisoup_simple", PF_R_AddTrisoup_Simple, 0}, {"getproperty", PF_R_GetViewFlag, 309}, // #309 vector/float(float property) getproperty (EXT_CSQC_1) @@ -6507,7 +6652,8 @@ static struct { {"getkeybind", PF_cl_getkeybind, 342}, // #342 string(float keynum) getkeybind (EXT_CSQC) {"setcursormode", PF_cl_setcursormode, 343}, // #343 This is originally a DP extension - {"getcursormode", PF_cl_getcursormode, 0}, // #343 This is originally a DP extension + {"getcursormode", PF_cl_getcursormode, 0}, // + {"setmousepos", PF_cl_setmousepos, 0}, // {"getmousepos", PF_cl_getmousepos, 344}, // #344 This is a DP extension {"getinputstate", PF_cs_getinputstate, 345}, // #345 float(float framenum) getinputstate (EXT_CSQC) @@ -7120,7 +7266,8 @@ void CSQC_Shutdown(void) if (csqc_deprecated_warned>1) { - Con_Printf("total %u csqc deprecation warnings suppressed\n", csqc_deprecated_warned-1); + if (!cl_csqc_nodeprecate.ival) + Con_Printf("total %u csqc deprecation warnings suppressed\n", csqc_deprecated_warned-1); csqc_deprecated_warned = 0; } } @@ -7478,7 +7625,7 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec csqc_builtin[BuiltinList[i].ebfsnum] = BuiltinList[i].bifunc; } - csqc_deprecated_warned = false; + csqc_deprecated_warned = !!cl_csqc_nodeprecate.ival; memset(cl.model_csqcname, 0, sizeof(cl.model_csqcname)); memset(cl.model_csqcprecache, 0, sizeof(cl.model_csqcprecache)); @@ -7981,6 +8128,8 @@ void CSQC_RegisterCvarsAndThings(void) Cvar_Register(&cl_csqcdebug, CSQCPROGSGROUP); Cvar_Register(&cl_nocsqc, CSQCPROGSGROUP); Cvar_Register(&pr_csqc_coreonerror, CSQCPROGSGROUP); + Cvar_Register(&cl_csqc_nodeprecate, CSQCPROGSGROUP); + Cvar_Register(&dpcompat_csqcinputeventtypes, CSQCPROGSGROUP); } void CSQC_CvarChanged(cvar_t *var) @@ -8039,6 +8188,7 @@ qboolean CSQC_DrawView(void) { int ticlimit = 10; float mintic = 0.01; + float maxtic = 0.1; double clframetime = host_frametime; RSpeedLocals(); @@ -8065,6 +8215,8 @@ qboolean CSQC_DrawView(void) } else { + if (csqc_world.rbe) + maxtic = mintic; //physics engines need a fixed tick rate. while(1) { host_frametime = cl.servertime - csqc_world.physicstime; @@ -8075,8 +8227,8 @@ qboolean CSQC_DrawView(void) csqc_world.physicstime = cl.servertime; break; } - if (host_frametime > mintic) - host_frametime = mintic; + if (host_frametime > maxtic) + host_frametime = maxtic; #ifdef USERBE if (csqc_world.rbe) @@ -8295,8 +8447,9 @@ qboolean CSQC_KeyPress(int key, int unicode, qboolean down, unsigned int devid) { static qbyte csqckeysdown[K_MAX]; void *pr_globals; + int ie = down?CSIE_KEYDOWN:CSIE_KEYUP; - if (!csqcprogs || !csqcg.input_event) + if (!csqcprogs || !csqcg.input_event || ie >= dpcompat_csqcinputeventtypes.ival) return false; #ifdef TEXTEDITOR if (editormodal) @@ -8304,7 +8457,7 @@ qboolean CSQC_KeyPress(int key, int unicode, qboolean down, unsigned int devid) #endif pr_globals = PR_globals(csqcprogs, PR_CURRENT); - G_FLOAT(OFS_PARM0) = down?CSIE_KEYDOWN:CSIE_KEYUP; + G_FLOAT(OFS_PARM0) = ie; G_FLOAT(OFS_PARM1) = MP_TranslateFTEtoQCCodes(key); G_FLOAT(OFS_PARM2) = unicode; G_FLOAT(OFS_PARM3) = devid; @@ -8338,7 +8491,7 @@ qboolean CSQC_MousePosition(float xabs, float yabs, unsigned int devid) { void *pr_globals; - if (!csqcprogs || !csqcg.input_event) + if (!csqcprogs || !csqcg.input_event || CSIE_MOUSEABS >= dpcompat_csqcinputeventtypes.ival) return false; pr_globals = PR_globals(csqcprogs, PR_CURRENT); @@ -8355,7 +8508,7 @@ qboolean CSQC_MouseMove(float xdelta, float ydelta, unsigned int devid) { void *pr_globals; - if (!csqcprogs || !csqcg.input_event) + if (!csqcprogs || !csqcg.input_event || CSIE_MOUSEDELTA >= dpcompat_csqcinputeventtypes.ival) return false; pr_globals = PR_globals(csqcprogs, PR_CURRENT); @@ -8372,7 +8525,7 @@ qboolean CSQC_MouseMove(float xdelta, float ydelta, unsigned int devid) qboolean CSQC_JoystickAxis(int axis, float value, unsigned int devid) { void *pr_globals; - if (!csqcprogs || !csqcg.input_event) + if (!csqcprogs || !csqcg.input_event || CSIE_JOYAXIS >= dpcompat_csqcinputeventtypes.ival) return false; pr_globals = PR_globals(csqcprogs, PR_CURRENT); @@ -8387,7 +8540,7 @@ qboolean CSQC_JoystickAxis(int axis, float value, unsigned int devid) qboolean CSQC_Accelerometer(float x, float y, float z) { void *pr_globals; - if (!csqcprogs || !csqcg.input_event) + if (!csqcprogs || !csqcg.input_event || CSIE_ACCELEROMETER >= dpcompat_csqcinputeventtypes.ival) return false; pr_globals = PR_globals(csqcprogs, PR_CURRENT); @@ -8401,7 +8554,7 @@ qboolean CSQC_Accelerometer(float x, float y, float z) qboolean CSQC_Gyroscope(float x, float y, float z) { void *pr_globals; - if (!csqcprogs || !csqcg.input_event) + if (!csqcprogs || !csqcg.input_event || CSIE_GYROSCOPE >= dpcompat_csqcinputeventtypes.ival) return false; pr_globals = PR_globals(csqcprogs, PR_CURRENT); diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 38a2e971..83741a20 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -110,6 +110,8 @@ struct { unsigned int owner; //kdm_foo. whoever has an interest in this font. font is purged when this becomes 0. char slotname[16]; char facename[MAX_OSPATH]; + float scale; //poop + int outline; //argh int sizes; int size[FONT_SIZES]; struct font_s *font[FONT_SIZES]; @@ -209,6 +211,8 @@ void PR_ReleaseFonts(unsigned int purgeowner) fontslot[i].sizes = 0; fontslot[i].slotname[0] = '\0'; fontslot[i].facename[0] = '\0'; + fontslot[i].scale = 1; + fontslot[i].outline = 0; } } void PR_ReloadFonts(qboolean reload) @@ -236,7 +240,7 @@ void PR_ReloadFonts(qboolean reload) { //otherwise load it. for (j = 0; j < fontslot[i].sizes; j++) { - fontslot[i].font[j] = Font_LoadFont(fontslot[i].facename, fontslot[i].size[j]); + fontslot[i].font[j] = Font_LoadFont(fontslot[i].facename, fontslot[i].size[j], fontslot[i].scale, fontslot[i].outline); } } } @@ -248,6 +252,7 @@ void QCBUILTIN PF_CL_findfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_g } void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + extern cvar_t r_font_postprocess_outline; const char *slotname = PR_GetStringOfs(prinst, OFS_PARM0); const char *facename = PR_GetStringOfs(prinst, OFS_PARM1); const char *sizestr = PR_GetStringOfs(prinst, OFS_PARM2); @@ -272,7 +277,7 @@ void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_g return; //if its changed, purge it. - if (stricmp(fontslot[slotnum].slotname, slotname) || stricmp(fontslot[slotnum].facename, facename)) + if (stricmp(fontslot[slotnum].slotname, slotname) || stricmp(fontslot[slotnum].facename, facename) || !fontslot[slotnum].sizes) { Q_strncpyz(fontslot[slotnum].slotname, slotname, sizeof(fontslot[slotnum].slotname)); Q_strncpyz(fontslot[slotnum].facename, facename, sizeof(fontslot[slotnum].facename)); @@ -282,14 +287,41 @@ void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_g Font_Free(fontslot[slotnum].font[i]); fontslot[slotnum].font[i] = NULL; } + fontslot[slotnum].sizes = 0; fontslot[slotnum].owner = 0; + fontslot[slotnum].scale = 1; + fontslot[slotnum].outline = r_font_postprocess_outline.ival; } fontslot[slotnum].owner |= world->keydestmask; while(*sizestr) { sizestr = COM_Parse(sizestr); + if (!strncmp(com_token, "scale=", 6)) + { + fontslot[slotnum].scale = atof(com_token+6); + continue; + } + if (!strncmp(com_token, "outline=", 8)) + { + fontslot[slotnum].outline = atoi(com_token+8); + continue; + } + if (!strncmp(com_token, "blur=", 5)) + { + //fontslot[slotnum].blur = atoi(com_token+5); + continue; + } + if (!strncmp(com_token, "voffset=", 8)) + { + //com_token+8 unused. + continue; + } + sz = atoi(com_token); + if (!sz) + continue; //o.O + for (i = 0; i < fontslot[slotnum].sizes; i++) { if (fontslot[slotnum].size[i] == sz) @@ -300,33 +332,45 @@ void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_g if (i >= FONT_SIZES) break; fontslot[slotnum].size[i] = sz; - if (qrenderer == QR_NONE) - fontslot[slotnum].font[i] = NULL; - else - fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i]); + fontslot[slotnum].font[i] = NULL; fontslot[slotnum].sizes++; } } + + if (qrenderer > QR_NONE) + { + for (i = 0; i < fontslot[slotnum].sizes; i++) + fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline); + } + G_FLOAT(OFS_RETURN) = slotnum; } #ifdef HAVE_LEGACY void CL_LoadFont_f(void) { + extern cvar_t r_font_postprocess_outline; //console command for compat with dp/debug. if (Cmd_Argc() == 1) { int i, j; + int th; for (i = 0; i < FONT_SLOTS; i++) { if (fontslot[i].sizes) { - Con_Printf("%s: %s (", fontslot[i].slotname, fontslot[i].facename); + Con_Printf("%s[%i]: %s (", fontslot[i].slotname, i, fontslot[i].facename); for (j = 0; j < fontslot[i].sizes; j++) { if (j) Con_Printf(", "); Con_Printf("%i", fontslot[i].size[j]); + if (fontslot[i].font[j]) + { + th = Font_GetTrueHeight(fontslot[i].font[j]); + if (th != Font_CharPHeight(fontslot[i].font[j])) + Con_Printf("[%g]", ((float)th*vid.height)/vid.pixelheight); + } } Con_Printf(")\n"); } @@ -339,7 +383,7 @@ void CL_LoadFont_f(void) char *slotname = Cmd_Argv(1); char *facename = Cmd_Argv(2); int sizenum = 3; - extern cvar_t dpcompat_console, gl_font; + extern cvar_t dpcompat_console, gl_font, con_textfont; //loadfont slot face size1 size2... @@ -378,6 +422,9 @@ void CL_LoadFont_f(void) fontslot[slotnum].font[i] = NULL; } fontslot[slotnum].owner = 0; + fontslot[slotnum].scale = 1; + fontslot[slotnum].sizes = 0; + fontslot[slotnum].outline = r_font_postprocess_outline.ival; //locked in at definition, so different fonts can have different settings even with vid_reload going on. } if (!*facename) return; @@ -389,11 +436,23 @@ void CL_LoadFont_f(void) int sz; if (!strcmp(a, "scale")) { + fontslot[slotnum].scale = atof(Cmd_Argv(sizenum++)); + continue; + } + if (!strcmp(a, "outline")) + { + fontslot[slotnum].outline = atoi(Cmd_Argv(sizenum++)); + continue; + } + if (!strcmp(a, "blur")) + { + //fontslot[slotnum].blur = atoi(Cmd_Argv(sizenum++)); sizenum++; continue; } if (!strcmp(a, "voffset")) { +// fontslot[slotnum].voffset = atof(Cmd_Argv(sizenum++)); sizenum++; continue; } @@ -411,16 +470,21 @@ void CL_LoadFont_f(void) if (i >= FONT_SIZES) break; fontslot[slotnum].size[i] = sz; - if (qrenderer == QR_NONE) - fontslot[slotnum].font[i] = NULL; - else - fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i]); + fontslot[slotnum].font[i] = NULL; fontslot[slotnum].sizes++; } } + if (qrenderer > QR_NONE) + { + for (i = 0; i < fontslot[slotnum].sizes; i++) + fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline); + } + //FIXME: slotnum0==default is problematic. - if (dpcompat_console.ival && (slotnum == 1 || (slotnum == 0 && !*gl_font.string))) + if (dpcompat_console.ival && slotnum == 1) + Cvar_Set(&con_textfont, facename); + if (dpcompat_console.ival && slotnum == 0) Cvar_Set(&gl_font, facename); } } @@ -1300,7 +1364,7 @@ static struct { func_t init; func_t shutdown; - func_t draw; + func_t draw; qboolean fuckeddrawsizes; func_t drawloading; func_t keydown; func_t keyup; @@ -1500,7 +1564,7 @@ static void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars char *funcname = NULL; int args; int builtinno; - if (prinst->GetFunctionInfo(prinst, funcref, &args, &builtinno, funcname, sizeof(funcname))) + if (prinst->GetFunctionInfo(prinst, funcref, &args, NULL, &builtinno, funcname, sizeof(funcname))) { //qc defines the function at least. nothing weird there... if (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins) { @@ -2282,7 +2346,7 @@ static struct { {"argescape", PF_argescape, 295}, //gap {"clearscene", PF_m_clearscene, 300}, - //no addentities +// {"addentities", PF_Fixme, 301}, {"addentity", PF_m_addentity, 302},//FIXME: needs setmodel, origin, angles, colormap(eep), frame etc, skin, #ifdef CSQC_DAT {"setproperty", PF_R_SetViewFlag, 303},//should be okay to share @@ -2310,15 +2374,28 @@ static struct { {"getkeybind", PF_cl_getkeybind, 342}, {"setcursormode", PF_cl_setcursormode, 343}, {"getcursormode", PF_cl_getcursormode, 0}, - //gap + {"setmousepos", PF_cl_setmousepos, 0}, +// {NULL, PF_Fixme, 344}, +// {NULL, PF_Fixme, 345}, +// {NULL, PF_Fixme, 346}, +// {NULL, PF_Fixme, 347}, +// {NULL, PF_Fixme, 348}, {"isdemo", PF_isdemo, 349}, +// {NULL, PF_Fixme, 350}, +// {NULL, PF_Fixme, 351}, {"registercommand", PF_menu_registercommand, 352}, - //gap + {"wasfreed", PF_WasFreed, 353}, +// {NULL, PF_Fixme, 354}, +#ifdef HAVE_MEDIA_DECODER + {"videoplaying", PF_cs_media_getstate, 355}, +#endif {"findfont", PF_CL_findfont, 356}, {"loadfont", PF_CL_loadfont, 357}, //gap // {"dynamiclight_get", PF_R_DynamicLight_Get, 372}, // {"dynamiclight_set", PF_R_DynamicLight_Set, 373}, +// {NULL, PF_Fixme, 374}, +// {NULL, PF_Fixme, 375}, {"setcustomskin", PF_m_setcustomskin, 376}, //gap {"memalloc", PF_memalloc, 384}, @@ -2336,6 +2413,15 @@ static struct { {"setwindowcaption", PF_cl_setwindowcaption, 0}, {"cvars_haveunsaved", PF_cvars_haveunsaved, 0}, //gap +// {"writebyte, PF_Fixme, 401}, +// {"writechar, PF_Fixme, 402}, +// {"writeshort, PF_Fixme, 403}, +// {"writelong, PF_Fixme, 404}, +// {"writeangle, PF_Fixme, 405}, +// {"writecoord, PF_Fixme, 406}, +// {"writestring, PF_Fixme, 407}, +// {"writeentity, PF_Fixme, 408}, + //gap {"buf_create", PF_buf_create, 440}, {"buf_del", PF_buf_del, 441}, {"buf_getsize", PF_buf_getsize, 442}, @@ -2346,7 +2432,7 @@ static struct { {"bufstr_set", PF_bufstr_set, 447}, {"bufstr_add", PF_bufstr_add, 448}, {"bufstr_free", PF_bufstr_free, 449}, - //450 +// {NULL, PF_Fixme, 450}, {"iscachedpic", PF_CL_is_cached_pic, 451}, {"precache_pic", PF_CL_precache_pic, 452}, {"free_pic", PF_CL_free_pic, 453}, @@ -2365,7 +2451,7 @@ static struct { {"cin_getstate", PF_cs_media_getstate, 464}, {"cin_restart", PF_cs_media_restart, 465}, #endif - {"drawline", PF_drawline, 466}, + {"drawline", PF_CL_drawline, 466}, {"drawstring", PF_CL_drawcolouredstring, 467}, {"stringwidth", PF_CL_stringwidth, 468}, {"drawsubpic", PF_CL_drawsubpic, 469}, @@ -2388,10 +2474,10 @@ static struct { {"strtolower", PF_strtolower, 480}, {"strtoupper", PF_strtoupper, 481}, {"cvar_defstring", PF_cvar_defstring, 482}, - //483 +// {NULL, PF_Fixme, 483}, {"strreplace", PF_strreplace, 484}, {"strireplace", PF_strireplace, 485}, - //486 +// {NULL, PF_Fixme, 486}, #ifdef HAVE_MEDIA_DECODER {"gecko_create", PF_cs_media_create, 487}, {"gecko_destroy", PF_cs_media_destroy, 488}, @@ -2400,7 +2486,7 @@ static struct { {"gecko_mousemove", PF_cs_media_mousemove, 491}, {"gecko_resize", PF_cs_media_resize, 492}, {"gecko_get_texture_extent",PF_cs_media_get_texture_extent,493}, - {"gecko_getproperty", PF_cs_media_getproperty}, + {"gecko_getproperty", PF_cs_media_getproperty, 0}, #endif {"crc16", PF_crc16, 494}, {"cvar_type", PF_cvar_type, 495}, @@ -2411,6 +2497,8 @@ static struct { {"entityfieldtype", PF_entityfieldtype, 498}, {"getentityfieldstring", PF_getentityfieldstring, 499}, {"putentityfieldstring", PF_putentityfieldstring, 500}, +// {NULL, PF_Fixme, 501}, +// {NULL, PF_Fixme, 502}, {"whichpack", PF_whichpack, 503}, //gap {"uri_escape", PF_uri_escape, 510}, @@ -2425,10 +2513,12 @@ static struct { {"cvar_description", PF_cvar_description, 518}, //gap {"log", PF_Logarithm, 532}, - //gap +// {"getsoundtime", PF_Fixme, 533}, {"soundlength", PF_soundlength, 534}, {"buf_loadfile", PF_buf_loadfile, 535}, {"buf_writefile", PF_buf_writefile, 536}, +// {"bufstr_find", PF_Fixme, 537}, +// {"matchpattern", PF_Fixme, 538}, //gap {"setkeydest", PF_cl_setkeydest, 601}, {"getkeydest", PF_cl_getkeydest, 602}, @@ -2462,7 +2552,8 @@ static struct { {"netaddress_resolve", PF_netaddress_resolve, 625}, {"getgamedirinfo", PF_cl_getgamedirinfo, 626}, {"sprintf", PF_sprintf, 627}, - //gap +// {NULL, PF_Fixme, 628}, +// {NULL, PF_Fixme, 629}, {"setkeybind", PF_cl_setkeybind, 630}, {"getbindmaps", PF_cl_GetBindMap, 631}, {"setbindmaps", PF_cl_SetBindMap, 632}, @@ -2471,8 +2562,10 @@ static struct { {"crypto_getencryptlevel", PF_crypto_getencryptlevel, 635}, {"crypto_getmykeyfp", PF_crypto_getmykeyfp, 636}, {"crypto_getmyidfp", PF_crypto_getmyidfp, 637}, +// {NULL, PF_Fixme, 638}, {"digest_hex", PF_digest_hex, 639}, {"digest_ptr", PF_digest_ptr, 0}, +// {NULL, PF_Fixme, 640}, {"crypto_getmyidstatus", PF_crypto_getmyidfp, 641}, @@ -2665,6 +2758,8 @@ static qboolean MP_KeyEvent(menu_t *menu, qboolean isdown, unsigned int devid, i } static void MP_TryRelease(menu_t *m) { + if (inmenuprogs) + return; //if the qc asked for it, the qc probably already knows about it. don't recurse. MP_Toggle(0); } @@ -2916,7 +3011,13 @@ qboolean MP_Init (void) mpfuncs.init = PR_FindFunction(menu_world.progs, "m_init", PR_ANY); mpfuncs.shutdown = PR_FindFunction(menu_world.progs, "m_shutdown", PR_ANY); - mpfuncs.draw = PR_FindFunction(menu_world.progs, "m_draw", PR_ANY); + { + int args = 0; + qbyte *argsizes = NULL; + mpfuncs.draw = PR_FindFunction(menu_world.progs, "m_draw", PR_ANY); + menu_world.progs->GetFunctionInfo(menu_world.progs, mpfuncs.draw, &args, &argsizes, NULL, NULL, 0); + mpfuncs.fuckeddrawsizes = (args == 2 && argsizes[0] == 1 && argsizes[1] == 1); + } mpfuncs.drawloading = PR_FindFunction(menu_world.progs, "m_drawloading", PR_ANY); mpfuncs.inputevent = PR_FindFunction(menu_world.progs, "Menu_InputEvent", PR_ANY); mpfuncs.keydown = PR_FindFunction(menu_world.progs, "m_keydown", PR_ANY); @@ -3085,21 +3186,36 @@ void MP_Draw(void) inmenuprogs++; pr_globals = PR_globals(menu_world.progs, PR_CURRENT); - ((float *)pr_globals)[OFS_PARM0+0] = vid.width; - ((float *)pr_globals)[OFS_PARM0+1] = vid.height; - ((float *)pr_globals)[OFS_PARM0+2] = 0; - ((float *)pr_globals)[OFS_PARM1+0] = vid.height; //dp compat, ish + if (scr_drawloading||scr_disabled_for_loading) { //don't draw the menu if we're meant to be drawing a loading screen //the menu should provide a special function if it wants to draw custom loading screens. this is for compat with old/dp/lazy/crappy menus. if (mpfuncs.drawloading) { + ((float *)pr_globals)[OFS_PARM0+0] = vid.width; + ((float *)pr_globals)[OFS_PARM0+1] = vid.height; + ((float *)pr_globals)[OFS_PARM0+2] = 0; + ((float *)pr_globals)[OFS_PARM1] = scr_disabled_for_loading; PR_ExecuteProgram(menu_world.progs, mpfuncs.drawloading); } } else if (mpfuncs.draw) + { + if (mpfuncs.fuckeddrawsizes) + { //pass useless sizes in two args if its a dp menu + ((float *)pr_globals)[OFS_PARM0] = vid.pixelwidth; + ((float *)pr_globals)[OFS_PARM1] = vid.pixelheight; + } + else + { //pass useful sizes in a 1-arg vector if its an fte menu. + ((float *)pr_globals)[OFS_PARM0+0] = vid.width; + ((float *)pr_globals)[OFS_PARM0+1] = vid.height; + ((float *)pr_globals)[OFS_PARM0+2] = 0; + } + PR_ExecuteProgram(menu_world.progs, mpfuncs.draw); + } inmenuprogs--; } diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 9817abb6..edfc432e 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -56,6 +56,7 @@ struct extern cvar_t scr_conalpha; extern cvar_t gl_conback; extern cvar_t gl_font, con_textfont; +extern cvar_t r_font_postprocess_outline; extern cvar_t gl_screenangle; extern cvar_t vid_conautoscale; extern cvar_t vid_conheight; @@ -137,6 +138,7 @@ void R2D_Shutdown(void) { Cvar_Unhook(&con_textfont); Cvar_Unhook(&gl_font); + Cvar_Unhook(&r_font_postprocess_outline); Cvar_Unhook(&vid_conautoscale); Cvar_Unhook(&gl_screenangle); Cvar_Unhook(&vid_conheight); @@ -195,6 +197,10 @@ void R2D_Shutdown(void) Z_Free(atlas.data); memset(&atlas, 0, sizeof(atlas)); + +#ifdef PLUGINS + Plug_FreeAllImages(); +#endif } /* @@ -396,6 +402,7 @@ void R2D_Init(void) Cvar_Hook(&con_textfont, R2D_Font_Callback); Cvar_Hook(&gl_font, R2D_Font_Callback); + Cvar_Hook(&r_font_postprocess_outline, R2D_Font_Callback); Cvar_Hook(&vid_conautoscale, R2D_Conautoscale_Callback); Cvar_Hook(&gl_screenangle, R2D_ScreenAngle_Callback); Cvar_Hook(&vid_conheight, R2D_Conheight_Callback); @@ -1107,9 +1114,9 @@ void R2D_Font_Changed(void) LOGFONTW lf = {0}; CHOOSEFONTW cf = {sizeof(cf)}; extern HWND mainwindow; - font_default = Font_LoadFont("", 8); + font_default = Font_LoadFont("", 8, 1, r_font_postprocess_outline.ival); if (tsize != 8) - font_console = Font_LoadFont("", tsize); + font_console = Font_LoadFont("", tsize, 1, r_font_postprocess_outline.ival); if (!font_console) font_console = font_default; @@ -1152,15 +1159,15 @@ void R2D_Font_Changed(void) #endif } - font_default = Font_LoadFont(gl_font.string, 8); + font_default = Font_LoadFont(gl_font.string, 8, 1, r_font_postprocess_outline.ival); if (!font_default && *gl_font.string) - font_default = Font_LoadFont("", 8); + font_default = Font_LoadFont("", 8, 1, r_font_postprocess_outline.ival); if (tsize != 8 || strcmp(gl_font.string, con_font_name)) { - font_console = Font_LoadFont(con_font_name, tsize); + font_console = Font_LoadFont(con_font_name, tsize, 1, r_font_postprocess_outline.ival); if (!font_console) - font_console = Font_LoadFont("", tsize); + font_console = Font_LoadFont("", tsize, 1, r_font_postprocess_outline.ival); } if (!font_console) font_console = font_default; diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 2cdd5ccf..235038f4 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -3764,6 +3764,7 @@ int Surf_NewLightmaps(int count, int width, int height, uploadfmt_t fmt, qboolea if (deluxe && (count & 1)) { deluxe = false; +// count+=1; Con_Print("WARNING: Deluxemapping with odd number of lightmaps\n"); } @@ -4190,6 +4191,11 @@ void Surf_NewMap (void) #endif int i; + //evil haxx + r_dynamic.ival = r_dynamic.value; + if (r_dynamic.ival > 0 && cl.worldmodel->fromgame == fg_quake3) //quake3 has no lightmaps, disable r_dynamic + r_dynamic.ival = 0; + memset (&r_worldentity, 0, sizeof(r_worldentity)); AngleVectors(r_worldentity.angles, r_worldentity.axis[0], r_worldentity.axis[1], r_worldentity.axis[2]); VectorInverse(r_worldentity.axis[1]); diff --git a/engine/client/render.h b/engine/client/render.h index f074de0c..3051d3bb 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -292,6 +292,7 @@ typedef struct float hdr_value; vec3_t skyroom_pos; /*the camera position for sky rooms*/ + vec4_t skyroom_spin; /*the camera spin for sky rooms*/ qboolean skyroom_enabled; /*whether a skyroom position is defined*/ int firstvisedict; /*so we can skip visedicts in skies*/ @@ -476,7 +477,7 @@ const char *Image_FormatName(uploadfmt_t encoding); qboolean Image_FormatHasAlpha(uploadfmt_t encoding); image_t *Image_LoadTexture (const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname, const char *fname, qbyte *filedata, int filesize); -void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename); +void Image_ChangeFormat(struct pendingtextureinfo *mips, qboolean *allowedformats, uploadfmt_t origfmt, const char *imagename); void *Image_FlipImage(const void *inbuffer, void *outbuffer, int *inoutwidth, int *inoutheight, int pixelbytes, qboolean flipx, qboolean flipy, qboolean flipd); #ifdef D3D8QUAKE @@ -650,6 +651,8 @@ extern cvar_t r_novis; extern cvar_t r_netgraph; extern cvar_t r_deluxemapping_cvar; extern qboolean r_deluxemapping; +extern qboolean r_fakeshadows; //enables the use of ortho model-only shadows +extern float r_blobshadows; extern cvar_t r_softwarebanding_cvar; extern qboolean r_softwarebanding; extern cvar_t r_lightprepass_cvar; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index c4632d3b..b6e90b09 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -73,6 +73,24 @@ static void QDECL R_Lightmap_Format_Changed(struct cvar_s *var, char *oldvalue) if (qrenderer) Surf_BuildLightmaps(); } +static void QDECL R_HDR_FramebufferFormat_Changed(struct cvar_s *var, char *oldvalue) +{ + int i; + char *e; + for (i = 0; i < PTI_MAX; i++) + { + if (!Q_strcasecmp(var->string, Image_FormatName(i))) + { + var->ival = -i; + return; + } + } + var->ival = strtol(var->string, &e, 0); + if (*e && e == var->string) + Con_Printf("%s set to unknown image format\n", var->name); + if (var->ival < 0) + var->ival = 0; +} #ifdef FTE_TARGET_WEB //webgl sucks too much to get a stable framerate without vsync. cvar_t vid_vsync = CVARAF ("vid_vsync", "1", @@ -99,7 +117,7 @@ cvar_t gl_part_flame = CVARFD ("gl_part_flame", "1", CVAR_ARCHIVE, "Enable //opengl library, blank means try default. static cvar_t gl_driver = CVARFD ("gl_driver", "", CVAR_ARCHIVE | CVAR_VIDEOLATCH, "Specifies the graphics driver name to load. This is typically a filename. Blank for default."); -static cvar_t vid_devicename = CVARFD ("vid_devicename", "", CVAR_ARCHIVE | CVAR_VIDEOLATCH, "Specifies which video device to try to use. If blank or invalid then one will be guessed."); +cvar_t vid_devicename = CVARFD ("vid_devicename", "", CVAR_ARCHIVE | CVAR_VIDEOLATCH, "Specifies which video device to try to use. If blank or invalid then one will be guessed."); cvar_t gl_shadeq1_name = CVARD ("gl_shadeq1_name", "*", "Rename all surfaces from quake1 bsps using this pattern for the purposes of shader names."); extern cvar_t r_vertexlight; extern cvar_t r_forceprogramify; @@ -177,7 +195,7 @@ cvar_t r_lightstylesmooth_limit = CVAR ("r_lightstylesmooth_limit", "2"); cvar_t r_lightstylespeed = CVAR ("r_lightstylespeed", "10"); cvar_t r_lightstylescale = CVAR ("r_lightstylescale", "1"); cvar_t r_lightmap_scale = CVARFD ("r_shadow_realtime_nonworld_lightmaps", "1", 0, "Scaler for lightmaps used when not using realtime world lighting. Probably broken."); -cvar_t r_hdr_framebuffer = CVARFD("r_hdr_framebuffer", "0", CVAR_ARCHIVE, "If enabled, the map will be rendered into a high-precision image framebuffer. This avoids issues with shaders that contribute more than 1 in any single pass (like overbrights)."); +cvar_t r_hdr_framebuffer = CVARFCD("r_hdr_framebuffer", "0", CVAR_ARCHIVE, R_HDR_FramebufferFormat_Changed, "If enabled, the map will be rendered into a high-precision image framebuffer. This avoids issues with shaders that contribute more than 1 in any single pass (like overbrights). Can also be set to the name of an image format, to force rendering to that format first - interesting formats are L8, RGB565, B10G11R11F, and others."); cvar_t r_hdr_irisadaptation = CVARF ("r_hdr_irisadaptation", "0", CVAR_ARCHIVE); cvar_t r_hdr_irisadaptation_multiplier = CVAR ("r_hdr_irisadaptation_multiplier", "2"); cvar_t r_hdr_irisadaptation_minvalue = CVAR ("r_hdr_irisadaptation_minvalue", "0.5"); @@ -286,7 +304,7 @@ cvar_t vid_fullscreen = CVARF ("vid_fullscreen", "2", CVAR_ARCHIVE|CVAR_VIDEOLATCH); cvar_t vid_height = CVARFD ("vid_height", "0", CVAR_ARCHIVE | CVAR_VIDEOLATCH, "The screen height to attempt to use, in physical pixels. 0 means use desktop resolution."); -cvar_t vid_multisample = CVARFD ("vid_multisample", "0", +cvar_t vid_multisample = CVARAFD ("vid_multisample", "0", "vid_samples", CVAR_ARCHIVE, "The number of samples to use for Multisample AntiAliasing (aka: msaa). A value of 1 explicitly disables it."); cvar_t vid_refreshrate = CVARF ("vid_displayfrequency", "0", CVAR_ARCHIVE | CVAR_VIDEOLATCH); @@ -437,6 +455,11 @@ cvar_t gl_texturemode2d = CVARFCD("gl_texturemode2d", "GL_LINEAR", CVAR_ARCHIVE | CVAR_RENDERERCALLBACK, Image_TextureMode_Callback, "Specifies how 2d images are sampled. format is a 3-tupple "); cvar_t r_font_linear = CVARF("r_font_linear", "1", 0); +cvar_t r_font_postprocess_outline = CVARFD("r_font_postprocess_outline", "0", 0, "Controls the number of pixels of dark borders to use around fonts."); + +#if defined(HAVE_LEGACY) && defined(AVAIL_FREETYPE) +cvar_t dpcompat_smallerfonts = CVARFD("dpcompat_smallerfonts", "0", 0, "Mimics DP's behaviour of using a smaller font size than was actually requested."); +#endif cvar_t vid_triplebuffer = CVARAFD ("vid_triplebuffer", "1", "gl_triplebuffer", CVAR_ARCHIVE, "Specifies whether the hardware is forcing tripplebuffering on us, this is the number of extra page swaps required before old data has been completely overwritten."); @@ -446,7 +469,7 @@ cvar_t r_portalonly = CVARD ("r_portalonly", "0", "Don't draw things whic cvar_t r_noaliasshadows = CVARF ("r_noaliasshadows", "0", CVAR_ARCHIVE); cvar_t r_lodscale = CVARFD ("r_lodscale", "5", CVAR_ARCHIVE, "Scales the level-of-detail reduction on models (for those that have lod)."); cvar_t r_lodbias = CVARFD ("r_lodbias", "0", CVAR_ARCHIVE, "Biases the level-of-detail on models (for those that have lod)."); -cvar_t r_shadows = CVARFD ("r_shadows", "0", CVAR_ARCHIVE, "Draw basic blob shadows underneath entities without using realtime lighting."); +cvar_t r_shadows = CVARFD ("r_shadows", "0", CVAR_ARCHIVE, "Draw basic blob shadows underneath entities without using realtime lighting."); cvar_t r_showbboxes = CVARD("r_showbboxes", "0", "Debugging. Shows bounding boxes. 1=ssqc, 2=csqc. Red=solid, Green=stepping/toss/bounce, Blue=onground."); cvar_t r_showfields = CVARD("r_showfields", "0", "Debugging. Shows entity fields boxes (entity closest to crosshair). 1=ssqc, 2=csqc."); cvar_t r_showshaders = CVARD("r_showshaders", "0", "Debugging. Shows the name of the (worldmodel) shader being pointed to."); @@ -985,6 +1008,10 @@ void Renderer_Init(void) Cvar_Register (&gl_texturemode, GLRENDEREROPTIONS); Cvar_Register (&gl_texturemode2d, GLRENDEREROPTIONS); Cvar_Register (&r_font_linear, GLRENDEREROPTIONS); + Cvar_Register (&r_font_postprocess_outline, GLRENDEREROPTIONS); +#if defined(HAVE_LEGACY) && defined(AVAIL_FREETYPE) + Cvar_Register (&dpcompat_smallerfonts, GLRENDEREROPTIONS); +#endif Cvar_Register (&gl_mipcap, GLRENDEREROPTIONS); Cvar_Register (&gl_texture_lodbias, GLRENDEREROPTIONS); Cvar_Register (&gl_texture_anisotropic_filtering, GLRENDEREROPTIONS); @@ -1391,7 +1418,7 @@ qboolean R_ApplyRenderer (rendererstate_t *newr) time = Sys_DoubleTime(); #ifndef NOBUILTINMENUS - M_RemoveAllMenus(true); +// M_RemoveAllMenus(true); #endif Media_CaptureDemoEnd(); R_ShutdownRenderer(true); @@ -1493,11 +1520,15 @@ qboolean R_ApplyRenderer_Load (rendererstate_t *newr) memcpy(host_basepal, default_quakepal, 768); } } + Validation_FileLoaded("gfx/palette.lmp", host_basepal, 768); { - qbyte *colormap = (qbyte *)FS_LoadMallocFile ("gfx/colormap.lmp", NULL); - if (colormap) + size_t csize; + qbyte *colormap = (qbyte *)FS_LoadMallocFile ("gfx/colormap.lmp", &csize); + + if (colormap && csize == VID_GRADES*256+1) { + Validation_FileLoaded("gfx/colormap.lmp", colormap, csize); j = VID_GRADES-1; data = colormap + j*256; vid.fullbright = 0; @@ -1595,6 +1626,24 @@ TRACE(("dbg: R_ApplyRenderer: initing mods\n")); Cvar_ForceSetValue(&vid_dpi_x, vid.dpi_x); Cvar_ForceSetValue(&vid_dpi_y, vid.dpi_y); +#if 0//def HAVE_LEGACY + { //if dp's vid_pixelheight cvar exists then lets force it to what we know. + //that said, some dp mods just end up trying to use a fov of 0('auto') when this is 0, which fixes all our fov woes, so don't break that if its explictly 0 (a bit of a hack, but quite handy). + //setting this properly seems to fuck over xonotic. + cvar_t *pixelheight = Cvar_FindVar("vid_pixelheight"); + if (pixelheight && (pixelheight->value || !*pixelheight->string)) + { + if (vid.dpi_x && vid.dpi_y) + { + float ipd_x = 1/vid.dpi_x; + float ipd_y = 1/vid.dpi_y; + Cvar_ForceSetValue(pixelheight, ipd_y/ipd_x); + } + else + Cvar_ForceSetValue(pixelheight, 1); + } + } +#endif TRACE(("dbg: R_ApplyRenderer: R_PreNewMap (how handy)\n")); Surf_PreNewMap(); @@ -2992,7 +3041,7 @@ void R_SetFrustum (float projmat[16], float viewmat[16]) //do far plane //fog will logically not actually reach 0, though precision issues will force it. we cut off at an exponant of -500 - if (r_refdef.globalfog.density && r_refdef.globalfog.alpha>=1 && r_fog_cullentities.ival) + if (r_refdef.globalfog.density && r_refdef.globalfog.alpha>=1 && r_fog_cullentities.ival && !r_refdef.globalfog.depthbias) { float culldist; float fog; @@ -3028,7 +3077,7 @@ void R_SetFrustum (float projmat[16], float viewmat[16]) p->normal[1] *= -scale; p->normal[2] *= -scale; // p->dist *= scale; - p->dist = DotProduct(r_origin, p->normal)-culldist; + p->dist = DotProduct(r_refdef.vieworg, p->normal)-culldist; p->type = PLANE_ANYZ; p->signbits = SignbitsForPlane (p); diff --git a/engine/client/sbar.c b/engine/client/sbar.c index 2d75d408..90c139a7 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -1246,7 +1246,7 @@ void Draw_TinyString (float x, float y, const qbyte *str) if (!font_tiny) { - font_tiny = Font_LoadFont("gfx/tinyfont", 8); + font_tiny = Font_LoadFont("gfx/tinyfont", 8, 1, 0); if (!font_tiny) return; } diff --git a/engine/client/screen.h b/engine/client/screen.h index c514f09e..5277ce82 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -122,13 +122,14 @@ typedef enum uploadfmt PTI_RGBA32F, //usually overkill //packed/misaligned formats: these are specified in native endian order (high bits listed first because that's how things are represented in hex), so may need byte swapping... PTI_A2BGR10, //mostly for rendertargets, might also be useful for overbight lightmaps. - PTI_E5BGR9, //mostly for fancy lightmaps PTI_B10G11R11F, //unshared exponents PTI_RGB565, //16bit alphaless format. PTI_RGBA4444, //16bit format (gl) PTI_ARGB4444, //16bit format (d3d) PTI_RGBA5551, //16bit alpha format (gl). PTI_ARGB1555, //16bit alpha format (d3d). +#define PTI_FIRSTCOMPRESSED PTI_E5BGR9 + PTI_E5BGR9, //mostly for fancy lightmaps (technically compressed, with a block size of 1...) //(desktop/tegra) compressed formats PTI_BC1_RGB, /*4bpp*/ PTI_BC1_RGB_SRGB, /*4bpp*/ @@ -271,7 +272,7 @@ void Font_Init(void); void Font_Shutdown(void); int Font_RegisterTrackerImage(const char *image); //returns a unicode char value that can be used to embed the char within a line of text. qboolean Font_TrackerValid(unsigned int imid); -struct font_s *Font_LoadFont(const char *fontfilename, float height); +struct font_s *Font_LoadFont(const char *fontfilename, float height, float scale, int outline); void Font_Free(struct font_s *f); void Font_BeginString(struct font_s *font, float vx, float vy, int *px, int *py); void Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py); /*avoid using*/ @@ -279,6 +280,7 @@ void Font_Transform(float vx, float vy, int *px, int *py); int Font_CharHeight(void); float Font_CharVHeight(struct font_s *font); int Font_CharPHeight(struct font_s *font); +int Font_GetTrueHeight(struct font_s *font); float Font_CharScaleHeight(void); int Font_CharWidth(unsigned int charflags, unsigned int codepoint); float Font_CharScaleWidth(unsigned int charflags, unsigned int codepoint); diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c index 2bf63941..5e266d5f 100644 --- a/engine/client/snd_al.c +++ b/engine/client/snd_al.c @@ -311,6 +311,7 @@ static void S_Info(void); static void S_Shutdown_f(void); */ +static cvar_t s_al_disable = CVARD("s_al_disable", "0", "When set, prevents initialisation of openal. Audio ouput can then fall back to platfrm-specific output which doesn't have any of the miscilaneous openal limitations or bugs."); static cvar_t s_al_debug = CVARD("s_al_debug", "0", "Enables periodic checks for OpenAL errors."); static cvar_t s_al_use_reverb = CVARD("s_al_use_reverb", "1", "Controls whether reverb effects will be used. Set to 0 to block them. Reverb requires gamecode to configure the reverb properties, other than underwater."); //static cvar_t s_al_max_distance = CVARFC("s_al_max_distance", "1000",0,OnChangeALSettings); @@ -536,6 +537,7 @@ static qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache static void QDECL OpenAL_CvarInit(void) { + Cvar_Register(&s_al_disable, SOUNDVARS); Cvar_Register(&s_al_debug, SOUNDVARS); Cvar_Register(&s_al_use_reverb, SOUNDVARS); // Cvar_Register(&s_al_max_distance, SOUNDVARS); @@ -638,7 +640,7 @@ static ssamplepos_t OpenAL_GetChannelPos(soundcardinfo_t *sc, channel_t *chan) } //schanged says the sample has changed, otherwise its merely moved around a little, maybe changed in volume, but nothing that will restart it. -static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned int schanged) +static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdatereason_t schanged) { oalinfo_t *oali = sc->handle; ALuint src; @@ -682,12 +684,12 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned } } oali->source[chnum] = src; - schanged = true; //should normally be true anyway, but hey + schanged |= CUR_EVERYTHING; //should normally be true anyway, but hey } PrintALError("pre start sound"); - if (schanged==true && src) + if ((schanged&CUR_SOUNDCHANGE) && src) palSourceStop(src); //reclaim any queued buffers @@ -710,7 +712,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned palGetSourcei(src, AL_SOURCE_STATE, &buf); if (buf != AL_PLAYING) { - schanged = true; + schanged |= CUR_EVERYTHING; if(sfx->loopstart != -1) chan->pos = sfx->loopstart<flags & CF_FORCELOOP) @@ -741,6 +743,8 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned cvolume = chan->master_vol/255.0f; if (!(chan->flags & CF_CL_ABSVOLUME)) cvolume *= volume.value*voicevolumemod; + else + cvolume *= mastervolume.value; //openal doesn't support loopstart (entire sample loops or not at all), so if we're meant to skip the first half then we need to stream it. stream = sfx->decoder.decodedata || sfx->loopstart > 0; @@ -823,7 +827,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned palGetSourcei(src, AL_SOURCE_STATE, &buf); if (buf != AL_PLAYING) - schanged = true; + schanged = CUR_EVERYTHING; } else { @@ -900,7 +904,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned if (schanged) { - if (schanged == 2 && chan->pos) + if (schanged == CUR_UPDATE && chan->pos) { //complex update, but not restart. pos contains an offset, rather than an absolute time. int cursample; palGetSourcei(src, AL_SAMPLE_OFFSET, &cursample); @@ -995,7 +999,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned } /*and start it up again*/ - if (schanged != 2) + if (schanged != CUR_UPDATE) palSourcePlay(src); } @@ -1030,6 +1034,8 @@ static qboolean OpenAL_InitLibrary(void) #endif #ifdef OPENAL_STATIC + if (s_al_disable.ival) + return false; return true; #else static dllfunction_t openalfuncs[] = @@ -1072,6 +1078,8 @@ static qboolean OpenAL_InitLibrary(void) {NULL} }; + if (s_al_disable.ival) + return false; if (COM_CheckParm("-noopenal")) return false; diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 4ed46b91..4b95df9d 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -72,10 +72,11 @@ int desired_bits = 16; int sound_started=0; +cvar_t mastervolume = CVARFD( "mastervolume", "1", CVAR_ARCHIVE, "Additional multiplier for all other sounds."); cvar_t bgmvolume = CVARAFD( "musicvolume", "0.3", "bgmvolume", CVAR_ARCHIVE, "Volume level for background music."); cvar_t volume = CVARAFD( "volume", "0.7", /*q3*/"s_volume",CVAR_ARCHIVE, - "Main volume level for all engine sound."); + "Volume level for game sounds (does not affect music, voice, or cinematics)."); cvar_t nosound = CVARFD( "nosound", "0", CVAR_ARCHIVE, "Disable all sound from the engine. Cannot be overriden by configs or anything if set via the -nosound commandline argument."); @@ -1174,6 +1175,7 @@ void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf) voicevolumemod = s_voip.lastspoke_any > realtime?snd_voip_ducking.value:1; + voicevolumemod *= mastervolume.value; if (!voipsendenable || (voipcodec != s_voip.enccodec && s_voip.cdriver)) { @@ -2287,6 +2289,7 @@ void S_Init (void) Cmd_AddCommand("soundcontrol", S_Control_f); Cvar_Register(&nosound, "Sound controls"); + Cvar_Register(&mastervolume, "Sound controls"); Cvar_Register(&volume, "Sound controls"); Cvar_Register(&precache, "Sound controls"); Cvar_Register(&loadas8bit, "Sound controls"); @@ -2651,7 +2654,7 @@ static void SND_AccumulateSpacialization(soundcardinfo_t *sc, channel_t *ch, vec int seat; if (ch->flags & CF_CL_ABSVOLUME) - volscale = 1; + volscale = mastervolume.value; else volscale = volume.value * voicevolumemod; @@ -2778,7 +2781,7 @@ static void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch) //sounds with absvolume ignore all volume etc cvars+settings if (ch->flags & CF_CL_ABSVOLUME) - volscale = 1; + volscale = mastervolume.value; else volscale = volume.value * voicevolumemod; @@ -2877,7 +2880,7 @@ static void S_UpdateSoundCard(soundcardinfo_t *sc, qboolean updateonly, channel_ int skip; int absstartpos = updateonly?target_chan->pos:0; extern cvar_t cl_demospeed; - int chanupdatetype = true; + chanupdatereason_t chanupdatetype = updateonly?CUR_UPDATE:CUR_EVERYTHING; if (!sfx) sfx = target_chan->sfx; @@ -2899,7 +2902,7 @@ static void S_UpdateSoundCard(soundcardinfo_t *sc, qboolean updateonly, channel_ // spatialize if (target_chan->sfx != sfx) - chanupdatetype = true; + chanupdatetype |= CUR_SOUNDCHANGE; memset (target_chan, 0, sizeof(*target_chan)); if (!origin) { @@ -2943,12 +2946,6 @@ static void S_UpdateSoundCard(soundcardinfo_t *sc, qboolean updateonly, channel_ target_chan->sfx = sfx; - if (updateonly && sc->ChannelUpdate) - { - chanupdatetype = 2; - absstartpos = 0; - } - target_chan->rate = ((1<rate/sc->sn.speed; if (target_chan->rate < 1) /*make sure the rate won't crash us*/ target_chan->rate = 1; @@ -3209,7 +3206,7 @@ static void S_StopSoundCard(soundcardinfo_t *sc, int entnum, int entchannel) { sc->channel[i].sfx = NULL; if (sc->ChannelUpdate) - sc->ChannelUpdate(sc, &sc->channel[i], true); + sc->ChannelUpdate(sc, &sc->channel[i], CUR_EVERYTHING); if (entchannel) break; } @@ -3255,7 +3252,7 @@ void S_StopAllSounds(qboolean clear) } if (sc->ChannelUpdate) - sc->ChannelUpdate(sc, &sc->channel[i], true); + sc->ChannelUpdate(sc, &sc->channel[i], CUR_EVERYTHING); } } @@ -3346,7 +3343,7 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) SND_Spatialize (scard, ss); if (scard->ChannelUpdate) - scard->ChannelUpdate(scard, ss, true); + scard->ChannelUpdate(scard, ss, CUR_EVERYTHING); } S_UnlockMixer(); @@ -3376,7 +3373,7 @@ void S_Music_Clear(sfx_t *onlyifsample) sc->channel[i].sfx = NULL; if (sc->ChannelUpdate) - sc->ChannelUpdate(sc, &sc->channel[i], true); + sc->ChannelUpdate(sc, &sc->channel[i], CUR_EVERYTHING); if (s && s->decoder.ended && !S_IsPlayingSomewhere(s)) //if we aint playing it elsewhere, free it compleatly. s->decoder.ended(s); @@ -3437,7 +3434,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) for (i = MUSIC_FIRST; i < MUSIC_STOP; i++) { - qboolean changed = false; + chanupdatereason_t changed = false; chan = &sc->channel[i]; if (!chan->sfx) { @@ -3448,7 +3445,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) chan->sfx = newmusic; chan->rate = 1<pos = (int)(time * sc->sn.speed) * chan->rate; - changed = true; + changed = CUR_EVERYTHING; chan->master_vol = bound(0, 1, 255); chan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = chan->master_vol; @@ -3474,7 +3471,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) chan->sfx = NULL; if (sc->ChannelUpdate) - sc->ChannelUpdate(sc, chan, true); + sc->ChannelUpdate(sc, chan, CUR_EVERYTHING); if (s && s->decoder.ended && !S_IsPlayingSomewhere(s)) //if we aint playing it elsewhere, free it compleatly. s->decoder.ended(s); @@ -3555,7 +3552,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) chan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = bound(0, chan->master_vol * (volume.value*voicevolumemod), 255); if (sc->ChannelUpdate) - sc->ChannelUpdate(sc, chan, (oldvol == 0) ^ (sc->ambientlevels[i] == 0)); + sc->ChannelUpdate(sc, chan, ((oldvol == 0) ^ (sc->ambientlevels[i] == 0))?CUR_EVERYTHING:CUR_SPACIALISEONLY); } #endif } @@ -3701,7 +3698,7 @@ static void S_Q2_AddEntitySounds(soundcardinfo_t *sc) SND_Spatialize(sc, c); if (c->sfx) - sc->ChannelUpdate(sc, c, false); + sc->ChannelUpdate(sc, c, CUR_SPACIALISEONLY); } else { //merge with any other ents, if we can @@ -3725,7 +3722,7 @@ static void S_Q2_AddEntitySounds(soundcardinfo_t *sc) { c->sfx = sfx; if (sc->ChannelUpdate) - sc->ChannelUpdate(sc, c, true); + sc->ChannelUpdate(sc, c, CUR_EVERYTHING); } } } @@ -3769,7 +3766,7 @@ static void S_UpdateCard(soundcardinfo_t *sc) { ch->sfx = NULL; if (sc->ChannelUpdate) - sc->ChannelUpdate(sc, ch, true); + sc->ChannelUpdate(sc, ch, CUR_EVERYTHING); } ch->vol[0] = ch->vol[1] = ch->vol[2] = ch->vol[3] = ch->vol[4] = ch->vol[5] = 0; continue; @@ -3779,7 +3776,7 @@ static void S_UpdateCard(soundcardinfo_t *sc) { if (ch->flags & CF_FOLLOW) SND_Spatialize(sc, ch); //update it a little - sc->ChannelUpdate(sc, ch, false); + sc->ChannelUpdate(sc, ch, CUR_SPACIALISEONLY); continue; } @@ -4341,7 +4338,7 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, si->channel[i].pos = 0; si->channel[i].master_vol = 255 * volume; if (si->ChannelUpdate) - si->ChannelUpdate(si, &si->channel[i], false); + si->ChannelUpdate(si, &si->channel[i], CUR_SPACIALISEONLY); break; } if (i == si->total_chans) //this one wasn't playing. @@ -4360,7 +4357,7 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, SND_Spatialize(si, c); if (si->ChannelUpdate) - si->ChannelUpdate(si, c, true); + si->ChannelUpdate(si, c, CUR_EVERYTHING); } } } diff --git a/engine/client/snd_mem.c b/engine/client/snd_mem.c index 92790178..09d58abb 100644 --- a/engine/client/snd_mem.c +++ b/engine/client/snd_mem.c @@ -943,6 +943,9 @@ static void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t forcedecode, siz } } } + + if (data) + Validation_FileLoaded(name, data, filesize); } if (!data) diff --git a/engine/client/sound.h b/engine/client/sound.h index 541bd53c..37ceb674 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -105,7 +105,7 @@ typedef struct #define CF_FORCELOOP 2 // forces looping. set on static sounds. #define CF_NOSPACIALISE 4 // these sounds are played at a fixed volume in both speakers, but still gets quieter with distance. //#define CF_PAUSED 8 // rate = 0. or something. -#define CF_CL_ABSVOLUME 16 // ignores volume cvar. this is ignored if received from the server because there's no practical way for the server to respect the client's preferences. +#define CF_CL_ABSVOLUME 16 // ignores volume cvar (but not mastervolume). this is ignored if received from the server because there's no practical way for the server to respect the client's preferences. //#define CF_SV_RESERVED CF_CL_ABSVOLUME #define CF_NOREVERB 32 // disables reverb on this channel, if possible. #define CF_FOLLOW 64 // follows the owning entity (stops moving if we lose track) @@ -298,7 +298,7 @@ extern cvar_t snd_nominaldistance; extern cvar_t loadas8bit; extern cvar_t bgmvolume; -extern cvar_t volume; +extern cvar_t volume, mastervolume; extern cvar_t snd_capture; extern float voicevolumemod; @@ -339,6 +339,14 @@ extern sounddriver pWAV_InitCard; extern sounddriver pAHI_InitCard; */ +typedef enum +{ + CUR_SPACIALISEONLY = 0, //for ticking over, respacialising, etc + CUR_UPDATE = (1u<<1), //flags/rate/offset changed without changing the sound itself + CUR_SOUNDCHANGE = (1u<<2), //the audio file changed too. reset everything. + CUR_EVERYTHING = CUR_UPDATE|CUR_SOUNDCHANGE +} chanupdatereason_t; + struct soundcardinfo_s { //windows has one defined AFTER directsound char name[256]; //a description of the card. char guid[256]; //device name as detected (so input code can create sound devices without bugging out too much) @@ -376,7 +384,7 @@ struct soundcardinfo_s { //windows has one defined AFTER directsound unsigned int (*GetDMAPos) (soundcardinfo_t *sc); //get the current point that the hardware is reading from (the return value should not wrap, at least not very often) void (*SetEnvironmentReverb) (soundcardinfo_t *sc, size_t reverb); //if you have eax enabled, change the environment. fixme. generally this is a stub. optional. void (*Restore) (soundcardinfo_t *sc); //called before lock/unlock/lock/unlock/submit. optional - void (*ChannelUpdate) (soundcardinfo_t *sc, channel_t *channel, unsigned int schanged); //properties of a sound effect changed. this is to notify hardware mixers. optional. + void (*ChannelUpdate) (soundcardinfo_t *sc, channel_t *channel, chanupdatereason_t schanged); //properties of a sound effect changed. this is to notify hardware mixers. optional. void (*ListenerUpdate) (soundcardinfo_t *sc, int entnum, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t velocity); //player moved or something. this is to notify hardware mixers. optional. ssamplepos_t (*GetChannelPos) (soundcardinfo_t *sc, channel_t *channel); //queries a hardware mixer's channel position (essentially returns channel->pos, except more up to date) diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index c9faa7b0..25e628f0 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -842,8 +842,6 @@ static void Friendly_Crash_Handler(int sig, siginfo_t *info, void *vcontext) strftime (buffer, sizeof(buffer), "Time: %Y-%m-%d %H:%M:%S\n",timeinfo); write(fd, buffer, strlen(buffer)); - Q_snprintfz(buffer, sizeof(buffer), "Binary: "__DATE__" "__TIME__"\n"); - write(fd, buffer, strlen(buffer)); Q_snprintfz(buffer, sizeof(buffer), "Ver: %i.%02i%s\n", FTE_VER_MAJOR, FTE_VER_MINOR, #ifdef OFFICIAL_RELEASE " (official)"); @@ -851,13 +849,17 @@ static void Friendly_Crash_Handler(int sig, siginfo_t *info, void *vcontext) ""); #endif write(fd, buffer, strlen(buffer)); -#ifdef SVNREVISION - if (strcmp(STRINGIFY(SVNREVISION), "-")) - { - Q_snprintfz(buffer, sizeof(buffer), "Revision: %s\n", STRINGIFY(SVNREVISION)); - write(fd, buffer, strlen(buffer)); - } + +#if defined(SVNREVISION) && defined(SVNDATE) + Q_snprintfz(buffer, sizeof(buffer), "Revision: %s, %s\n", STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE)); +#else + Q_snprintfz(buffer, sizeof(buffer), + #ifdef SVNREVISION + "Revision: "STRINGIFY(SVNREVISION)"\n" + #endif + "Binary: "__DATE__" "__TIME__"\n"); #endif + write(fd, buffer, strlen(buffer)); backtrace_symbols_fd(array + firstframe, size - firstframe, fd); write(fd, "\n", 1); @@ -1088,11 +1090,20 @@ int main (int c, const char **v) newtime = Sys_DoubleTime (); time = newtime - oldtime; - sleeptime = Host_Frame(time); - oldtime = newtime; + if (isDedicated) + { + sleeptime = SV_Frame(); + oldtime = newtime; + NET_Sleep(sleeptime, noconinput?false:true); + } + else + { + sleeptime = Host_Frame(time); + oldtime = newtime; - if (sleeptime) - Sys_Sleep(sleeptime); + if (sleeptime) + Sys_Sleep(sleeptime); + } } } diff --git a/engine/client/sys_sdl.c b/engine/client/sys_sdl.c index 8d741398..e2ffdc2a 100644 --- a/engine/client/sys_sdl.c +++ b/engine/client/sys_sdl.c @@ -317,113 +317,209 @@ void Sys_Quit (void) //SDL provides no file enumeration facilities. #if defined(_WIN32) #include -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) +static int Sys_EnumerateFiles2 (const char *match, int matchstart, int neststart, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath) { + qboolean go; + HANDLE r; - WIN32_FIND_DATA fd; - char apath[MAX_OSPATH]; - char apath2[MAX_OSPATH]; - char file[MAX_OSPATH]; - char *s; - int go; - if (!gpath) - return 0; -// strcpy(apath, match); - Q_snprintfz(apath, sizeof(apath), "%s/%s", gpath, match); - for (s = apath+strlen(apath)-1; s> apath; s--) + WIN32_FIND_DATAW fd; + int nest = neststart; //neststart refers to just after a / + qboolean wild = false; + + while(match[nest] && match[nest] != '/') { - if (*s == '/') - break; + if (match[nest] == '?' || match[nest] == '*') + wild = true; + nest++; } - *s = '\0'; - - //this is what we ask windows for. - Q_snprintfz(file, sizeof(file), "%s/*.*", apath); - - //we need to make apath contain the path in match but not gpath - Q_strncpyz(apath2, match, sizeof(apath)); - match = s+1; - for (s = apath2+strlen(apath2)-1; s> apath2; s--) + if (match[nest] == '/') { - if (*s == '/') - break; - } - *s = '\0'; - if (s != apath2) - strcat(apath2, "/"); + char submatch[MAX_OSPATH]; + char tmproot[MAX_OSPATH]; + + if (!wild) + return Sys_EnumerateFiles2(match, matchstart, nest+1, func, parm, spath); + + if (nest-neststart+1> MAX_OSPATH) + return 1; + memcpy(submatch, match+neststart, nest - neststart); + submatch[nest - neststart] = 0; + nest++; + + if (neststart+4 > MAX_OSPATH) + return 1; + memcpy(tmproot, match, neststart); + strcpy(tmproot+neststart, "*.*"); - r = FindFirstFile(file, &fd); - if (r==(HANDLE)-1) - return 1; - go = true; - do - { - if (*fd.cFileName == '.'); //don't ever find files with a name starting with '.' - else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory { - if (wildcmp(match, fd.cFileName)) - { - Q_snprintfz(file, sizeof(file), "%s%s/", apath2, fd.cFileName); - go = func(file, fd.nFileSizeLow, 0, parm, spath); - } + wchar_t wroot[MAX_OSPATH]; + r = FindFirstFileExW(widen(wroot, sizeof(wroot), tmproot), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, 0); } - else + strcpy(tmproot+neststart, ""); + if (r==(HANDLE)-1) + return 1; + go = true; + do { - if (wildcmp(match, fd.cFileName)) + char utf8[MAX_OSPATH]; + char file[MAX_OSPATH]; + narrowen(utf8, sizeof(utf8), fd.cFileName); + if (*utf8 == '.'); //don't ever find files with a name starting with '.' + else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory { - Q_snprintfz(file, sizeof(file), "%s%s", apath2, fd.cFileName); - go = func(file, fd.nFileSizeLow, 0, parm, spath); + if (wildcmp(submatch, utf8)) + { + int newnest; + if (strlen(tmproot) + strlen(utf8) + strlen(match+nest) + 2 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s/", tmproot, utf8); + newnest = strlen(file); + strcpy(file+newnest, match+nest); + go = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath); + } + } } - } + } while(FindNextFileW(r, &fd) && go); + FindClose(r); } - while(FindNextFile(r, &fd) && go); - FindClose(r); + else + { + const char *submatch = match + neststart; + char tmproot[MAX_OSPATH]; + if (neststart+4 > MAX_OSPATH) + return 1; + memcpy(tmproot, match, neststart); + strcpy(tmproot+neststart, "*.*"); + + { + wchar_t wroot[MAX_OSPATH]; + r = FindFirstFileExW(widen(wroot, sizeof(wroot), tmproot), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, 0); + } + strcpy(tmproot+neststart, ""); + if (r==(HANDLE)-1) + return 1; + go = true; + do + { + char utf8[MAX_OSPATH]; + char file[MAX_OSPATH]; + + narrowen(utf8, sizeof(utf8), fd.cFileName); + if (*utf8 == '.') + ; //don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files) + else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory + { + if (wildcmp(submatch, utf8)) + { + if (strlen(tmproot+matchstart) + strlen(utf8) + 2 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s/", tmproot+matchstart, utf8); + go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), 0, parm, spath); + } + } + } + else + { + if (wildcmp(submatch, utf8)) + { + if (strlen(tmproot+matchstart) + strlen(utf8) + 1 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s", tmproot+matchstart, utf8); + go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), 0, parm, spath); + } + } + } + } while(FindNextFileW(r, &fd) && go); + FindClose(r); + } return go; } +int Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath) +{ + char fullmatch[MAX_OSPATH]; + int start; + if (strlen(gpath) + strlen(match) + 2 > MAX_OSPATH) + return 1; + + strcpy(fullmatch, gpath); + start = strlen(fullmatch); + if (start && fullmatch[start-1] != '/') + fullmatch[start++] = '/'; + fullmatch[start] = 0; + strcat(fullmatch, match); + return Sys_EnumerateFiles2(fullmatch, start, start, func, parm, spath); +} #elif defined(linux) || defined(__unix__) || defined(__MACH__) #include -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) +#include +static int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) { DIR *dir; - char apath[MAX_OSPATH]; char file[MAX_OSPATH]; - char truepath[MAX_OSPATH]; - char *s; + const char *s; struct dirent *ent; struct stat st; + const char *wild; + const char *apath = truepath+apathofs; -//printf("path = %s\n", gpath); -//printf("match = %s\n", match); - - if (!gpath) - gpath = ""; - *apath = '\0'; - - Q_strncpyz(apath, match, sizeof(apath)); - for (s = apath+strlen(apath)-1; s >= apath; s--) + //if there's a * in a system path, then we need to scan its parent directory to figure out what the * expands to. + //we can just recurse quicklyish to try to handle it. + wild = strchr(apath, '*'); + if (!wild) + wild = strchr(apath, '?'); + if (wild) { - if (*s == '/') + char subdir[MAX_OSPATH]; + for (s = wild+1; *s && *s != '/'; s++) + ; + while (wild > truepath) { - s[1] = '\0'; - match += s - apath+1; - break; + if (*(wild-1) == '/') + break; + wild--; } + memcpy(file, truepath, wild-truepath); + file[wild-truepath] = 0; + + dir = opendir(file); + memcpy(subdir, wild, s-wild); + subdir[s-wild] = 0; + if (dir) + { + do + { + ent = readdir(dir); + if (!ent) + break; + if (*ent->d_name != '.') + { +#ifdef _DIRENT_HAVE_D_TYPE + if (ent->d_type != DT_DIR && ent->d_type != DT_UNKNOWN) + continue; +#endif + if (wildcmp(subdir, ent->d_name)) + { + memcpy(file, truepath, wild-truepath); + Q_snprintfz(file+(wild-truepath), sizeof(file)-(wild-truepath), "%s%s", ent->d_name, s); + if (!Sys_EnumerateFiles2(file, apathofs, match, func, parm, spath)) + { + closedir(dir); + return false; + } + } + } + } while(1); + closedir(dir); + } + return true; } - if (s < apath) //didn't find a '/' - *apath = '\0'; - - Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath); -//printf("truepath = %s\n", truepath); -//printf("gamepath = %s\n", gpath); -//printf("apppath = %s\n", apath); -//printf("match = %s\n", match); dir = opendir(truepath); if (!dir) { - Con_DPrintf("Failed to open dir %s\n", truepath); + Con_DLPrintf((errno==ENOENT)?2:1, "Failed to open dir %s\n", truepath); return true; } do @@ -443,6 +539,7 @@ int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const if (!func(file, st.st_size, st.st_mtime, parm, spath)) { +// Con_DPrintf("giving up on search after finding %s\n", file); closedir(dir); return false; } @@ -453,9 +550,35 @@ int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const } } while(1); closedir(dir); - return true; } +int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) +{ + char apath[MAX_OSPATH]; + char truepath[MAX_OSPATH]; + char *s; + + if (!gpath) + gpath = ""; + *apath = '\0'; + + Q_strncpyz(apath, match, sizeof(apath)); + for (s = apath+strlen(apath)-1; s >= apath; s--) + { + if (*s == '/') + { + s[1] = '\0'; + match += s - apath+1; + break; + } + } + if (s < apath) //didn't find a '/' + *apath = '\0'; + + Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath); + return Sys_EnumerateFiles2(truepath, strlen(gpath)+1, match, func, parm, spath); +} + #else int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, void *), void *parm, void *spath) { diff --git a/engine/client/textedit.c b/engine/client/textedit.c index e5c9cf9b..9a4df183 100644 --- a/engine/client/textedit.c +++ b/engine/client/textedit.c @@ -950,13 +950,76 @@ console_t *Con_TextEditor(const char *fname, const char *line, pubprogfuncs_t *d file = FS_OpenVFS(fname, "rb", FS_GAME); if (file) { - char buffer[65536]; - while (VFS_GETS(file, buffer, sizeof(buffer))) + unsigned char buffer[65536]; +#ifdef HAVE_LEGACY + if (!strcmp(".bin", COM_GetFileExtension(fname, NULL)) && VFS_GETLEN(file) == 80*25*2) { - Con_PrintCon(con, buffer, PFS_FORCEUTF8|PFS_KEEPMARKUP|PFS_NONOTIFY); - Con_PrintCon(con, "\n", PFS_FORCEUTF8|PFS_KEEPMARKUP|PFS_NONOTIFY); + static unsigned short ibmtounicode[256] = + { + 0x0000,0x263A,0x263B,0x2665,0x2666,0x2663,0x2660,0x2022,0x25D8,0x25CB,0x25D9,0x2642,0x2640,0x266A,0x266B,0x263C, //0x00(non-ascii, display-only) + 0x25BA,0x25C4,0x2195,0x203C,0x00B6,0x00A7,0x25AC,0x21A8,0x2191,0x2193,0x2192,0x2190,0x221F,0x2194,0x25B2,0x25BC, //0x10(non-ascii, display-only) + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,0x002D,0x002E,0x002F, //0x20(ascii) + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F, //0x30(ascii) + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F, //0x40(ascii) + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005A,0x005B,0x005C,0x005D,0x005E,0x005F, //0x50(ascii) + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F, //0x60(ascii) + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007A,0x007B,0x007C,0x007D,0x007E,0x2302, //0x70(mostly ascii, one display-only) + 0x00C7,0x00FC,0x00E9,0x00E2,0x00E4,0x00E0,0x00E5,0x00E7,0x00EA,0x00EB,0x00E8,0x00EF,0x00EE,0x00EC,0x00C4,0x00C5, //0x80(non-ascii, printable) + 0x00C9,0x00E6,0x00C6,0x00F4,0x00F6,0x00F2,0x00FB,0x00F9,0x00FF,0x00D6,0x00DC,0x00A2,0x00A3,0x00A5,0x20A7,0x0192, //0x90(non-ascii, printable) + 0x00E1,0x00ED,0x00F3,0x00FA,0x00F1,0x00D1,0x00AA,0x00BA,0x00BF,0x2310,0x00AC,0x00BD,0x00BC,0x00A1,0x00AB,0x00BB, //0xa0(non-ascii, printable) + 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255D,0x255C,0x255B,0x2510, //0xb0(box-drawing, printable) + 0x2514,0x2534,0x252C,0x251C,0x2500,0x253C,0x255E,0x255F,0x255A,0x2554,0x2569,0x2566,0x2560,0x2550,0x256C,0x2567, //0xc0(box-drawing, printable) + 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256B,0x256A,0x2518,0x250C,0x2588,0x2584,0x258C,0x2590,0x2580, //0xd0(box-drawing, printable) + 0x03B1,0x00DF,0x0393,0x03C0,0x03A3,0x03C3,0x00B5,0x03C4,0x03A6,0x0398,0x03A9,0x03B4,0x221E,0x03C6,0x03B5,0x2229, //0xe0(maths(greek), printable) + 0x2261,0x00B1,0x2265,0x2264,0x2320,0x2321,0x00F7,0x2248,0x00B0,0x2219,0x00B7,0x221A,0x207F,0x00B2,0x25A0,0x00A0, //0xf0(maths, printable) + }; + int c, l, len; + unsigned int u, code, oldcode; + char line[7*80+2]; + char tohex[] = "0123456789ABCDEF"; + VFS_READ(file, buffer, 80*25*2); + VFS_CLOSE(file); + for (l = 0; l < 25*(80*2); l+=80*2) + { + for(c = 0, len = 0, oldcode = ~0; c < 80; c++) + { + u = buffer[l+c*2]; + code = buffer[l+c*2+1]; + if (oldcode != code) + { + oldcode = code; + line[len++] = '^'; + line[len++] = '&'; + line[len++] = tohex[(code&0xf)>>0]; + line[len++] = tohex[(code&0x70)>>4]; + } + + u = ibmtounicode[u]; //convert from dos's IBM437 codepage to unicode. + if (u == 0 || u == '\t' || u == '\n' || u == '\r') + line[len++] = ' '; + else if (u == '^') + { + line[len++] = '^'; + line[len++] = '^'; + } + else + len += utf8_encode(&line[len], u, 3); + } + line[len++] = '\n'; + line[len] = 0; + Con_PrintCon(con, line, PFS_FORCEUTF8|PFS_NONOTIFY); + } + } + else +#endif + { + while (VFS_GETS(file, buffer, sizeof(buffer))) + { + Con_PrintCon(con, buffer, PFS_FORCEUTF8|PFS_KEEPMARKUP|PFS_NONOTIFY); + Con_PrintCon(con, "\n", PFS_FORCEUTF8|PFS_KEEPMARKUP|PFS_NONOTIFY); + } + VFS_CLOSE(file); } - VFS_CLOSE(file); } } for (l = con->oldest; l; l = l->newer) diff --git a/engine/client/valid.c b/engine/client/valid.c index 6162c1f6..1024cb51 100644 --- a/engine/client/valid.c +++ b/engine/client/valid.c @@ -5,13 +5,6 @@ #include "winquake.h" #endif -typedef struct f_modified_s { - char name[MAX_QPATH]; - qboolean ismodified; - struct f_modified_s *next; -} f_modified_t; - -static f_modified_t *f_modified_list; qboolean care_f_modified; qboolean f_modified_particles; @@ -22,17 +15,19 @@ static void QDECL rulesetcallback(cvar_t *var, char *oldval) cvar_t allow_f_version = CVAR("allow_f_version", "1"); cvar_t allow_f_server = CVAR("allow_f_server", "1"); -cvar_t allow_f_modified = CVAR("allow_f_modified", "1"); cvar_t allow_f_skins = CVAR("allow_f_skins", "1"); cvar_t allow_f_ruleset = CVAR("allow_f_ruleset", "1"); cvar_t allow_f_scripts = CVAR("allow_f_scripts", "1"); +#ifdef HAVE_LEGACY +cvar_t allow_f_modified = CVAR("allow_f_modified", "1"); cvar_t allow_f_fakeshaft = CVAR("allow_f_fakeshaft", "1"); +cvar_t auth_validateclients = CVAR("auth_validateclients", "1"); +#endif cvar_t allow_f_system = CVAR("allow_f_system", "0"); cvar_t allow_f_cmdline = CVAR("allow_f_cmdline", "0"); -cvar_t auth_validateclients = CVAR("auth_validateclients", "1"); cvar_t ruleset = CVARCD("ruleset", "none", rulesetcallback, "Known rulesets are:\nnone: no explicit rules, all 'minor cheats' are allowed.\nstrict: equivelent to the smackdown ruleset. Note that this will block certain graphical enhancements too."); - +#ifdef HAVE_LEGACY #define SECURITY_INIT_BAD_CHECKSUM 1 #define SECURITY_INIT_BAD_VERSION 2 #define SECURITY_INIT_ERROR 3 @@ -62,6 +57,7 @@ static Security_Shutdown_t Security_Shutdown; #if 0//def _WIN32 static void *secmodule; #endif +#endif static void Validation_Version(void) { @@ -92,6 +88,7 @@ static void Validation_Version(void) if (!allow_f_version.ival) return; //suppress it +#ifdef HAVE_LEGACY if (Security_Generate_Crc) { signed_buffer_t *resp; @@ -103,6 +100,7 @@ static void Validation_Version(void) Q_snprintfz(auth, sizeof(authbuf), " crc: %s", resp->buf); } else +#endif auth = ""; if (*sr) @@ -112,6 +110,7 @@ static void Validation_Version(void) } void Validation_CheckIfResponse(char *text) { +#ifdef HAVE_LEGACY //client name, version type(os-renderer where it matters, os/renderer where renderer doesn't), 12 char hex crc int f_query_client; int i; @@ -193,21 +192,25 @@ void Validation_CheckIfResponse(char *text) else// if (!resp) Con_Printf(CON_ERROR "AUTHENTICATION FAILED.\n"); } +#endif } void InitValidation(void) { Cvar_Register(&allow_f_version, "Authentication"); Cvar_Register(&allow_f_server, "Authentication"); +#ifdef HAVE_LEGACY Cvar_Register(&allow_f_modified, "Authentication"); + Cvar_Register(&allow_f_fakeshaft, "Authentication"); +#endif Cvar_Register(&allow_f_skins, "Authentication"); Cvar_Register(&allow_f_ruleset, "Authentication"); - Cvar_Register(&allow_f_fakeshaft, "Authentication"); Cvar_Register(&allow_f_scripts, "Authentication"); Cvar_Register(&allow_f_system, "Authentication"); Cvar_Register(&allow_f_cmdline, "Authentication"); Cvar_Register(&ruleset, "Authentication"); +#ifdef HAVE_LEGACY #if 0//def _WIN32 secmodule = LoadLibrary("fteqw-security.dll"); if (secmodule) @@ -251,30 +254,279 @@ void InitValidation(void) Security_IsModelModified = NULL; Security_Supported_Binaries = NULL; Security_Shutdown = NULL; +#endif } ////////////////////// //f_modified -void Validation_IncludeFile(char *filename, char *file, int filelen) +#ifdef HAVE_LEGACY +#define FMOD_DM 1 +#define FMOD_TF 2 +static const struct { + const char *name; + unsigned int flags; + unsigned int hashes; + const char *hash; +} modifiles[] = +/*Note: I don't know what that 'debug' package refers to, these are just the hashes from ezquake.*/ { -} - + {"progs/armor.mdl", FMOD_DM | FMOD_TF, 4, "\xef\xb8\xd1\x18\x73\xd9\x43\xfe\x13\x43\x10\xbb\x90\x90\x6a\xef\x96\x86\x04\x2b" //quake + "\xfc\x4f\x26\x8d\x7c\x1e\xbb\xfa\xbc\x28\x11\xc8\x32\x3d\x63\x21\x4f\x59\x0b\xfa" //debug + "\x2a\xc5\x72\x77\x13\x7c\xe3\xde\x23\x85\xbc\xbb\xba\x25\x85\x34\x49\xd6\x1b\x6e" //ruohis + "\xb2\xe7\x47\x56\x46\x57\x46\x52\xfa\x20\x03\xc1\x0c\xc9\x92\x1e\x72\x7c\x57\x27"},//plaguespack + {"progs/backpack.mdl", FMOD_DM | FMOD_TF, 2, "\xeb\xda\xce\x80\x82\xd2\xf7\x18\xc2\xe7\x11\xa9\xaa\x09\xe6\xff\x05\x60\x0a\x05" //quake + "\x82\xf3\xc1\xe7\x2e\xc2\x3d\x0c\xc0\x04\x1a\xd3\x52\xed\x51\x72\x23\xf2\xba\x45"},//debug + {"progs/bolt2.mdl", FMOD_DM | FMOD_TF, 1, "\xde\xa8\xfb\x14\xe4\x7a\x23\x99\x43\x60\x80\xc8\x54\xa4\xee\xeb\xa9\x99\x4a\x02"},//quake + {"progs/end1.mdl", FMOD_DM | FMOD_TF, 2, "\x9f\xd1\xfe\xd1\x32\xc6\x67\x5d\xe6\xa0\x72\x1d\xd7\x39\xab\x14\xc4\x35\xf4\x4b" //quake + "\x22\x93\xff\x51\x3d\xb8\x31\x8b\xbf\xbe\x88\x89\xe7\xdc\x17\x09\x04\x8e\x57\xd9"},//ruohis + {"progs/end2.mdl", FMOD_DM | FMOD_TF, 3, "\x20\xe6\xee\x98\x79\xcd\x7a\x10\x0d\x62\x31\x83\x48\x18\x9a\x2a\x1a\x9e\xd2\x64" //quake + "\x10\xb9\xa1\xe3\x6a\xe3\xc4\x28\x21\x93\xf6\x1a\x99\xdf\x19\x9d\xcb\xbf\xce\x5b" //unknown + "\x4b\x1e\xa7\x2e\x22\xcf\xf6\x0e\xa5\x3b\xd5\x86\x3d\x43\x45\x11\x54\x20\x71\xe6"},//ruohis + {"progs/end3.mdl", FMOD_DM | FMOD_TF, 3, "\x6d\x33\x1e\x7f\xb0\x4b\x4f\xe0\x1a\x5a\xc2\x4a\xe7\xdd\xae\x2c\x19\x33\x97\x09" //quake + "\x10\xb9\xa1\xe3\x6a\xe3\xc4\x28\x21\x93\xf6\x1a\x99\xdf\x19\x9d\xcb\xbf\xce\x5b" //unknown + "\xa0\xed\xc3\x04\x34\x25\x4f\x2b\xca\xbb\x7a\x12\xcf\x97\x5c\x75\x97\x65\x5b\x13"},//ruohis + {"progs/end4.mdl", FMOD_DM | FMOD_TF, 3, "\xaa\x72\xb5\xa4\x8d\xc3\x00\xdd\xa2\x8d\x0f\x13\x55\x0e\x7f\x79\x76\x2b\xa5\xcc" //quake + "\x10\xb9\xa1\xe3\x6a\xe3\xc4\x28\x21\x93\xf6\x1a\x99\xdf\x19\x9d\xcb\xbf\xce\x5b" //unkown + "\xc1\x54\x5a\x4d\xba\xaa\xa8\x1f\x2b\x5b\x0a\x60\x3f\xff\x08\x75\xde\x03\xaf\xc7"},//ruohis + {"progs/eyes.mdl", FMOD_DM | FMOD_TF, 1, "\xa8\x25\x6f\x27\x82\xf7\xb8\xc6\x52\x5d\xf7\x3e\x3e\x16\x1d\x91\x57\xe5\x79\x5d"},//quake + {"progs/g_light.mdl", FMOD_DM | FMOD_TF, 4, "\x66\x93\x9b\xcb\x91\x4d\xd7\x1f\xf6\x82\x6a\x9a\x3e\x2e\x6b\x5d\xac\xf5\x58\xc4" //quake + "\x78\xb4\x78\x9d\x49\xc8\x44\xfb\x9d\x6d\x6b\xc5\x39\x78\x73\xab\x48\xd8\xcf\x1f" //debug + "\x92\xb8\x31\x62\x16\xd6\x39\x1f\x5d\x71\x42\x14\x26\x28\xfc\xfb\xd6\xd8\x66\xc8" //plague + "\xb8\x50\x2d\xfe\xb4\x6e\xd5\x6a\xeb\xfc\x79\xe5\x6d\x6f\xe4\x43\xda\x16\xf6\x82"},//ruohis + {"progs/g_nail.mdl", FMOD_DM | FMOD_TF, 5, "\x71\x9f\x20\x7b\x0f\xd0\x7c\xa7\x53\x49\xf2\x91\x4b\x26\x2f\x93\x40\x74\x0e\x35" //quake + "\x5d\x9f\x61\xc6\x85\x1a\x51\x55\x63\xcc\xe0\x6d\x1a\x17\x61\xef\x73\xb1\xb1\x36" //debug + "\xc6\x31\x2e\xcb\xab\x64\x10\x1f\x81\xe6\x0b\x5e\x42\x86\x65\x5e\xf4\x1e\x41\xd9" //plague + "\x7e\xa3\x7d\xbe\x2b\x27\x31\xb5\x9a\xe8\x7d\x0c\xbd\x87\xf3\x26\xee\x31\xdc\x20" //ruohis + "\x64\x19\xdd\x86\x85\x6f\xe6\xeb\x4c\x5b\x7e\xf2\xda\xae\x30\x32\x60\x38\xc1\x10"},//pdp + {"progs/g_nail2.mdl", FMOD_DM | FMOD_TF, 4, "\x99\x44\xcb\x97\x1a\xe1\xe3\x88\xca\x6e\xec\xec\x8f\x6c\x3f\x88\x0c\xcf\x7e\xac" //quake + "\x69\x83\xd5\x9c\x27\x85\x4c\xe5\xb4\x34\xa2\xce\x3e\xd9\x2b\x47\x55\x86\xfa\x7d" //debug + "\x01\xe4\x3f\x7d\x44\x43\x8a\x73\x39\x24\xb6\x4a\xeb\x13\x73\xba\x36\x85\x6f\x6a" //plague + "\xca\x15\xdf\x38\x50\x43\xdb\xe2\x41\x58\x06\x5c\x8c\x54\xec\x67\xb2\xf0\xb3\x3d"},//ruohis + {"progs/g_rock.mdl", FMOD_DM | FMOD_TF, 5, "\x83\xdd\x17\x90\x4c\x95\x0d\x15\x44\x45\x5f\x9b\x72\x3b\x84\x10\x8e\x06\x97\x46" //quake + "\x53\x92\x4e\x33\x52\xa2\xa5\xa5\x56\xa8\xb9\x68\x47\x66\x22\x5e\xc7\xee\xba\xe4" //debug + "\xb0\xc7\x1f\xe3\x18\x06\x20\x35\x3c\x97\xa6\x8b\x55\xc5\x96\x12\xde\x1b\x54\xb2" //plague + "\x9a\xe2\xe3\xd2\xcf\x2a\x3a\x1e\x53\x1d\xf2\xa2\xdd\x2a\x45\x60\xfa\x2a\x73\xd8" //ruohis + "\xfe\x90\xe9\x10\xd2\x1e\x40\x3b\xad\x71\x9d\x9a\x59\xf0\x85\x90\x8f\x08\x10\x77"},//pdp + {"progs/g_rock2.mdl", FMOD_DM | FMOD_TF, 5, "\x20\xec\x47\x5a\xdc\x1c\x21\xd0\x60\xaf\xb8\xd6\xab\x3e\x81\xaf\x5b\x0b\x33\xba" //quake + "\xf9\x10\x4e\xe2\x41\xbc\x53\x0f\x2b\xee\x43\x60\xec\x7e\x57\x3d\x4c\x12\x75\xbc" //debug + "\xe9\xb3\x4e\x67\x27\x59\x32\xde\x37\x43\xbd\xda\x5c\x75\x7a\xc9\xf9\xf1\xf4\x97" //plagur + "\x58\x0c\x35\x54\x88\xfd\x09\x6a\x80\x4b\x21\xae\xde\x71\x1d\xc7\xe6\x0f\x9d\x10" //ruohis + "\xd9\x80\x42\x3c\x56\x3a\xa4\xd8\xeb\x31\xf9\xef\xaf\x10\x63\xb7\xad\x39\x8c\xb2"},//pdp + {"progs/g_shot.mdl", FMOD_DM | FMOD_TF, 5, "\xe1\x35\xa7\x35\x90\x60\xee\xc1\xb5\x40\x89\x9f\x1c\xfd\xde\x6c\x67\x1d\xec\x7e" //quake + "\x28\xa8\xbb\x7b\x98\x8f\x43\x99\x47\x37\x5e\x97\x2b\x8a\xbc\x6c\xb7\x4d\xa6\xd3" //debug + "\x58\x63\x48\xd3\x37\x3d\x3a\x4a\xe4\x43\xfc\x0e\x89\x2f\xa4\x55\x19\x85\x07\xf8" //plague + "\xd8\xaf\x4c\xc7\x02\xf9\x3a\xbc\x88\xb5\x52\xbb\x30\xca\x6f\x6f\x54\xb5\x2a\x5b" //ruohis + "\x5f\x01\x9f\x3a\x9e\xa2\x17\xd8\xfa\x87\x7a\xdc\x28\x16\xe3\xc6\x19\x11\x99\xaa"},//pdp + {"progs/gib1.mdl", FMOD_DM | FMOD_TF, 3, "\xa4\x9e\xdc\x99\x4a\xf7\x9b\x6e\x1e\x0a\x71\x25\x7b\xc7\x1f\x70\x92\x70\x77\x09" //quake + "\xfd\x06\xee\x69\x83\x84\xac\x8b\x3e\xa4\xc5\xf9\x22\x37\x51\xd7\xff\xa4\xd5\x55" //debug + "\xf8\x57\xff\x05\x96\xa0\x73\x10\x55\xd0\xe5\xc7\x0b\x04\x6c\x9c\x8a\xeb\xd2\x96"},//ruohis + {"progs/gib2.mdl", FMOD_DM | FMOD_TF, 3, "\x9b\xe2\xc3\x5a\xd9\x58\x63\xd6\x7a\xd2\x44\x10\xad\x48\xda\xb3\xbb\x9f\x1e\x5f" //quake + "\xdf\xa1\x51\x32\x08\x82\xe6\x50\x97\xf7\xf0\xef\x71\x4e\x89\x89\xe3\x5b\x50\x65" //debug + "\x87\xd6\x23\xdf\x47\x90\xea\x51\x97\x34\xdb\xbf\xdb\x63\x7b\xed\xfb\x1b\x1a\x13"},//ruohis + {"progs/gib3.mdl", FMOD_DM | FMOD_TF, 3, "\x61\x8e\x55\xc6\x63\x4f\xea\x13\x45\xda\xc9\x20\x2e\x21\x40\x06\x50\xf3\x98\x7b" //quake + "\x46\x0b\x8f\x79\x50\x72\x5c\xe5\xf5\xf3\x2f\x88\x80\x5a\x49\x75\x99\xed\xa3\x19" //debug + "\x1e\xf0\x5d\xff\x2f\x95\x06\x76\x84\xc7\x36\xdd\x33\x2d\xd7\xa0\x33\xfa\x6a\x06"},//ruohis + {"progs/grenade.mdl", FMOD_DM | FMOD_TF, 4, "\xb8\xff\xdf\x60\x0c\x1f\x87\xfc\x25\xc3\xf3\xd9\xaf\xdc\xaa\x61\xbf\x7c\xc3\x0e" //quake + "\x12\xdb\xf5\xda\x02\xfc\xd4\x41\x5a\xd3\x4d\x76\x88\x08\x49\xa4\xea\x6c\xd2\xd5" //debug + "\x10\xb9\xa1\xe3\x6a\xe3\xc4\x28\x21\x93\xf6\x1a\x99\xdf\x19\x9d\xcb\xbf\xce\x5b" //plague + "\x1e\x57\xe5\xec\xe8\x61\x5d\xa3\x49\xdf\xb6\xe2\xd9\x72\x53\xf3\x10\x89\xc7\x7a"},//ruohis + {"progs/invisibl.mdl", FMOD_DM | FMOD_TF, 3, "\x89\xf3\xe2\x23\x7f\x65\x79\x84\x25\x0d\x7e\x43\xae\x0b\x10\xee\x75\xa7\xd6\xba" //quake + "\x60\xae\xac\xe5\xfd\xe8\x2f\x8b\x78\x8e\xef\xb8\xe4\x6a\x23\x8d\xe3\x0b\xdb\xc3" //debug + "\x7e\x7e\x50\xb7\x19\x70\xdc\x84\x39\x1c\x5f\x8f\x79\x29\xdf\xc0\xdd\x93\x6b\xe8"},//ruohis + {"progs/invulner.mdl", FMOD_DM | FMOD_TF, 3, "\x75\xe1\x7e\x98\x35\x4f\x0d\xfd\x1c\x64\xd2\x06\xc8\x0d\x5c\x72\x7a\x53\x1f\x87" //quake + "\x3e\x0a\xb0\x57\x6d\xfa\x9a\x00\xcb\xc8\xc2\xa4\xcc\xec\xb0\xa7\x49\x70\xe5\xa9" //debug + "\x18\xa1\xcc\xdd\x41\x3d\x14\x9e\x57\xdc\xa8\xa2\xb7\x4e\x74\x82\x1b\x30\xaf\x09"},//ruohis + {"progs/missile.mdl", FMOD_DM | FMOD_TF, 4, "\xe8\xee\xdf\x9a\xc1\x72\x58\x18\xf8\x36\xbb\xb3\xab\x29\x6e\x99\xa9\xb2\x6a\xd4" //quake + "\x78\xa0\xe7\x2a\xd4\x93\x93\xc3\x88\x67\x57\x73\xd2\x99\x26\x24\xfd\x0b\x19\x8f" //debug + "\xec\xb3\x47\xe0\xe2\xd2\x03\xad\x07\x62\x14\x2a\xdf\xf2\xe1\x99\x42\x9f\x22\xfb" //plague + "\xca\x4a\x84\x7e\xf9\x7e\xb0\xb1\xd8\x94\x89\x3d\x4e\xd1\xb4\xe6\x58\x98\xc4\x56"},//ruohis + {"progs/quaddama.mdl", FMOD_DM | FMOD_TF, 3, "\x63\xf6\x60\x27\x05\x84\xdc\x32\xdf\x63\x75\x05\xa7\xc3\x14\x96\x9b\x94\x25\x01" //quake + "\x56\xa1\x10\x90\xdb\xad\x63\x1b\xe3\xd9\x9b\xbc\x4e\x6e\x8d\xff\x60\x12\xcd\xce" //debug + "\x6c\xcf\x93\xdb\xb8\xa0\x06\x70\x56\x8c\xb2\x90\xa7\xfb\x7a\xf9\xcb\x99\x36\x42"},//ruohis + {"progs/s_spike.mdl", FMOD_DM | FMOD_TF, 4, "\xe5\xf8\x08\xf3\xe2\x42\xc2\xcd\x1f\xb0\x71\x4f\x0a\x88\xb9\xaf\x9f\x8e\x19\x52" //quake + "\xcc\xe4\x59\xb1\xf0\xcc\x5d\xbc\xab\x93\x6e\x65\x24\xdd\x72\x3e\xc6\x6f\x44\x10" //debug + "\x11\x1b\xc6\xe7\x30\x7f\x3a\x70\xda\xa5\x51\x00\xd1\x5b\x4a\xb8\xac\x45\x36\xe2" //plague + "\xc5\x84\xbf\x40\x9c\x5a\xb8\xca\x24\xaa\x8b\x49\xc6\xd9\x07\xbe\x56\x67\x66\x6b"},//ruohis + {"progs/spike.mdl", FMOD_DM | FMOD_TF, 4, "\xaf\xad\xd9\xeb\x28\x2f\x3b\xfb\x34\x2c\xcc\x67\x1a\xc2\x6e\x92\x33\xa2\xe1\x09" //quake + "\x44\xb9\x8b\xe7\xe4\x53\xa7\x92\x6b\x22\x5c\x43\x5e\xa6\x21\x40\x6b\x8c\x38\xef" //debug + "\x95\xcb\xf1\x28\x91\xed\xb8\xaf\xff\x00\x83\x6a\x3f\xc0\x29\xeb\xcb\xbb\xa2\x28" //plague + "\x80\x59\x22\xd8\xf7\xe9\x99\x02\x66\xfd\x32\x67\x64\x52\x55\x54\x03\xa4\xbd\x67"},//ruohis + {"progs/suit.mdl", FMOD_DM | FMOD_TF, 2, "\xdd\xb9\xdc\xb7\x3b\xa0\x8d\xed\x5e\xfc\x6e\x41\x5a\x8d\xe3\x8e\x25\xbf\x63\x40" //quake + "\xf3\xe4\x4d\xbf\xc1\x27\x09\xe6\x00\x4f\x82\xf4\x56\xef\xfe\x21\x97\xa8\x00\xac"},//ruohis + {"progs/player.mdl", FMOD_DM, 2, "\xb4\x0a\xca\x95\x2e\xe6\x1b\x02\xa5\xe9\x55\x66\x1c\xef\xa7\xd4\x2f\x58\x84\xb4" //quake + "\x59\x34\x22\xc0\x8d\x7d\xb9\x42\x72\xf4\x2f\xc5\x10\x07\xee\xf3\x32\x11\xe2\x41"},//capnbubs + {"progs/player.mdl", FMOD_TF, 1, "\x58\xb6\xca\x8f\xef\x97\x7a\x02\x3d\xee\x6e\xa9\x46\x4f\xe4\xc1\xc9\x33\xa5\x18"},//tf + {"progs/tf_flag.mdl", FMOD_TF, 1, "\xf0\x9d\x96\x6e\xef\x9e\x1b\xc9\x40\x1a\xcb\x84\x2e\x12\xee\xca\x28\x3d\x1d\x1b"},//tf + {"progs/turrbase.mdl", FMOD_TF, 1, "\xd5\x73\xda\x0c\xcb\x03\x27\x82\x6b\xbe\x6c\x9e\xf7\x95\xfb\x94\xed\x4b\xc0\xc7"},//tf + {"progs/turrgun.mdl", FMOD_TF, 1, "\x6f\x12\x4b\x31\xb5\x7c\x8d\x7d\xc8\x85\xf1\x8d\xf7\x62\x36\x1f\x7b\x95\x72\xa2"},//tf + {"progs/disp.mdl", FMOD_TF, 1, "\x4a\xfa\x96\x55\x57\x24\x4e\xd9\xdf\x84\x0f\x14\xa8\xdd\x59\x58\xfc\x52\x67\xe9"},//tf + {"progs/tf_stan.mdl", FMOD_TF, 1, "\x1b\x2a\x73\x1b\xc2\x69\x30\x76\x70\xe2\x79\xe7\xaa\x9d\x2b\x25\x2a\xdb\xf7\xdd"},//tf + {"progs/hgren2.mdl", FMOD_TF, 1, "\x5f\x71\xc3\x1d\x0f\x3c\xc5\x58\xfc\x04\x15\xdb\xdf\x4b\x35\xab\x97\xee\xac\x6a"},//tf + {"progs/grenade2.mdl", FMOD_TF, 1, "\xef\x6c\x11\x4b\x74\xf0\xc6\xeb\x73\xea\xcd\x2a\x16\x87\x6a\x87\x9d\x40\xe7\xe5"},//tf + {"progs/detpack.mdl", FMOD_TF, 1, "\xe1\xd4\x73\xaf\x38\xdc\x98\x0c\xc0\x83\x39\x6d\x33\x00\xa7\xd7\x5e\x67\x6a\x61"},//tf + {"progs/s_bubble.spr", FMOD_DM | FMOD_TF, 1, "\xf2\xfa\xbe\x3d\x54\x86\xb3\x73\x74\x07\x91\x70\xf3\x85\xe2\xdf\x0e\xd9\xb5\x4d"},//quake + {"progs/s_explod.spr", FMOD_DM | FMOD_TF, 1, "\x06\x7b\x5a\x29\x88\x1f\xdf\xac\x94\x08\xa2\x50\x5f\x90\x75\x5c\x0b\x5d\xd9\xb9"},//quake + {"maps/b_bh100.bsp", FMOD_DM | FMOD_TF, 4, "\x02\xe3\xe7\x65\x4d\x5b\xa8\x94\x74\xe6\x92\x80\xf0\xe5\x00\xf7\xcc\x7f\x66\xde" //quake + "\xb6\xd5\x7f\x52\x7f\x70\x90\xd4\x22\x96\xd6\xab\x69\x8a\xd1\xf3\xb9\x0e\xbc\xe9" //ruohis1 + "\xc1\x35\x21\xb4\x9c\x42\x16\x68\x3e\xce\xe3\x21\xfc\xf8\xb9\xfa\xe1\x7a\x38\x09" //ruohis2 + "\xff\x55\xb4\xc6\x45\xb8\x7c\xad\x29\x67\x35\xa7\xcf\x5f\x37\x35\x79\xb4\xad\x10"},//generations + {"sound/buttons/airbut1.wav", FMOD_DM | FMOD_TF, 2, "\x1e\x14\xd5\x47\xeb\xe8\x25\xc9\x3c\x58\xe5\x26\x21\xd0\xdf\xc8\xef\x92\x67\x22" //quake + "\xe0\xbc\xfb\xd5\x31\xe4\x83\x20\x4d\xc5\x11\xaa\x53\xe0\x9c\x08\x11\xce\x03\xdf"},//mindgrind + {"sound/items/armor1.wav", FMOD_DM | FMOD_TF, 2, "\xb0\x8d\x48\x44\x1d\x0a\x0b\xef\xb4\xa8\xcd\x3a\x67\xb4\x87\x3d\xcc\x4f\xdd\xe4" //quake + "\xed\xda\x47\x1b\x6a\xcd\x60\x5a\x99\x04\x94\x83\x1b\x65\xeb\x65\xcf\xf6\x41\x44"},//mindgrind + {"sound/items/damage.wav", FMOD_DM | FMOD_TF, 2, "\xac\x78\x77\x19\xc5\x52\xa2\x92\x56\x02\xc3\x90\x64\xf2\xa6\x7b\x4f\x65\xab\x56" //quake + "\xa3\x87\xa0\x3d\xc7\xd4\x99\x17\x31\xbf\xda\x8d\x6f\xde\xe2\x42\xec\xab\xfd\x5b"},//mindgrind + {"sound/items/damage2.wav", FMOD_DM | FMOD_TF, 2, "\x4d\x9b\x9a\x71\x18\xb2\x76\x1c\x9f\x97\xbf\xfc\xfa\xe6\xa5\x5e\x0e\xda\xaf\x68" //quake + "\x9c\x77\xeb\xd0\x3c\xd8\xf1\x0b\x7e\x1c\x0e\x12\x9b\x44\xfa\x50\x5b\x65\xd4\x2c"},//mindgrind + {"sound/items/damage3.wav", FMOD_DM | FMOD_TF, 2, "\x6c\xde\x07\xfa\x39\x6d\x30\x6e\xed\xf3\x18\x7d\x58\x25\x27\x90\x7a\x1e\xd0\x63" //quake + "\x71\x3b\x0c\x2e\xc9\xcf\x52\xdd\xe5\xe1\x5f\x8e\xd9\x8f\xc4\x0a\x70\xb9\xd8\xa2"},//mindgrind + {"sound/items/health1.wav", FMOD_DM | FMOD_TF, 1, "\xb2\x17\xdd\xeb\x80\x9b\x28\xa1\xf2\xe3\x10\x78\xbf\x01\x8d\xdb\x96\x5a\x49\x0b"},//quake + {"sound/items/inv1.wav", FMOD_DM | FMOD_TF, 2, "\xb8\x40\x12\xa0\x30\xd4\x88\xb5\xe0\x06\x24\xc6\xfd\x9d\xe5\x39\x98\x4b\x5a\xad" //quake + "\xba\x08\x77\x78\x51\x2c\xf5\x63\x32\x59\x83\x55\x86\x6d\xb4\x7d\xcc\x30\xc8\xcc"},//mindgrind + {"sound/items/inv2.wav", FMOD_DM | FMOD_TF, 2, "\x5d\x02\xb6\x9a\xa2\x24\x9d\x2d\x7c\xb1\x27\xc1\x8a\x90\x9e\x01\xd3\xf7\x21\xa5" //quake + "\x8c\xbc\xa3\xb2\x5f\x6b\xb5\x7e\x67\x4d\x5e\x67\x40\x40\x05\x24\x0b\xcd\x2e\x3b"},//mindgrind + {"sound/items/inv3.wav", FMOD_DM | FMOD_TF, 2, "\x77\x57\x78\xfb\x26\x28\x62\x9c\x5c\xd0\x21\x61\x61\x56\x2d\xf4\x29\x54\x4a\xa3" //quake + "\x33\x54\x94\xa1\x52\x2c\x3e\x7f\x8a\x52\x44\x91\xf0\x1c\x89\x02\x55\xb2\x5e\xd9"},//mindgrind + {"sound/items/itembk2.wav", FMOD_DM | FMOD_TF, 2, "\x9b\x51\x8c\x17\x27\x05\x03\x3b\xd2\xae\x9a\x75\xd7\xa7\xdc\xf7\x36\x1e\x1a\xf0" //quake + "\xad\x6d\xdd\x41\x4b\xc2\xa8\x45\x5e\xb7\x56\xef\x5a\x91\xb3\x5a\x0f\xa7\x01\x29"},//mindgrind + {"sound/player/land.wav", FMOD_DM | FMOD_TF, 1, "\x41\x5e\xbb\xb8\x8e\xad\x87\xf0\xd5\x3c\x32\x13\x52\x20\x2d\x2e\x38\x9e\x8e\x33"},//quake + {"sound/player/land2.wav", FMOD_DM | FMOD_TF, 1, "\x5c\xb5\x13\x6c\x89\x15\x6c\xc4\x42\xac\xab\xee\x4e\xd2\x7c\x08\x86\x23\x55\xd9"},//quake + {"sound/misc/outwater.wav", FMOD_DM | FMOD_TF, 2, "\x36\xfc\xb6\x9c\xba\xe9\x20\x9c\x18\x84\x5f\x59\x9f\x6d\xe7\x50\xfd\x3d\x50\xa7" //quake + "\x15\x85\xe9\x6b\x26\x01\xab\xfe\x11\xc8\xed\x80\x12\x70\xcf\xe7\x80\x49\xef\x1d"},//mindgrind + {"sound/weapons/pkup.wav", FMOD_DM | FMOD_TF, 2, "\x23\x35\xa4\x05\x60\xab\xbb\x09\xa0\x67\xce\x77\x3d\xe2\x2f\xb5\x01\x57\x71\xf2" //quake + "\xbc\x1f\x26\xda\x68\x7c\xf4\x12\x94\xe7\x91\x9e\x4d\x43\x0a\xce\x77\x5e\xe3\xc4"},//mindgrind + {"sound/player/plyrjmp8.wav", FMOD_DM | FMOD_TF, 2, "\x46\xe9\xe2\x75\xf2\x54\xad\xc9\x03\x7b\xd4\x6c\xd4\xc9\xf0\xee\xda\x86\x33\x5d" //quake + "\x63\x82\x9e\x5a\x6e\xf0\xb3\xb9\xe1\x7d\x04\x8a\xec\x8b\xc1\x72\x77\xf9\x7d\x54"},//mindgrind + {"sound/items/protect.wav", FMOD_DM | FMOD_TF, 2, "\x51\x13\xff\x38\xc7\x99\x67\x7d\x6d\x25\x77\x75\x92\x31\x23\xbf\xaa\x3b\x22\xb6" //quake + "\x3f\x33\x48\x59\x2a\x88\x8e\xa2\x64\x29\xb8\xb4\xc5\x79\x06\xe1\xb1\x8c\x1f\x19"},//mindgrind + {"sound/items/protect2.wav", FMOD_DM | FMOD_TF, 2, "\x59\xd1\x8d\x58\x2c\x9f\x48\xae\x8e\x11\xf7\x22\x4c\xeb\x7e\x48\x97\xc9\x54\x3c" //quake + "\xb0\x77\xe9\x29\xfe\x78\x93\x0d\x37\x90\x48\xb4\x62\x3d\xd8\x02\x8d\x71\x0b\xce"},//mindgrind + {"sound/items/protect3.wav", FMOD_DM | FMOD_TF, 2, "\x43\x11\x9a\x86\x04\xa4\xe6\xc9\x0e\xd9\xd5\xcb\x4c\x7e\xb5\xa1\x20\xdd\x77\xae" //quake + "\x4c\x31\x19\x76\x1c\x90\xb6\x07\xe2\x01\x06\x00\x7e\x36\x58\xc3\xb1\x90\x9f\x99"},//mindgrind + {"sound/items/r_item1.wav", FMOD_DM | FMOD_TF, 2, "\x41\x29\xe1\xdd\x04\xb8\xb3\xfb\xfa\xf5\x6c\x77\xea\xf8\x1d\xe8\x63\x9f\x42\xa1" //quake + "\xd4\x54\x57\x0f\x5f\x4c\xb8\xa8\x3a\x90\x01\x00\x9c\x28\xab\xd3\x48\xc8\x3b\xa9"},//mindgrind + {"sound/items/r_item2.wav", FMOD_DM | FMOD_TF, 4, "\x47\x40\xda\xd9\x1a\x19\x7b\x5a\x0d\x86\x2d\xc0\xde\x79\xf6\x18\x3a\xd9\x7b\xc4" //quake + "\xd2\x00\x2a\xf6\xca\xaa\xce\x5f\x92\x16\xf9\x25\xb7\x2c\x60\xf7\x25\xa5\x0d\x23" //us + "\x46\xe6\xa8\x83\x13\xc3\x4c\xbc\x6e\xa9\x7a\xa0\xda\xea\x81\x9d\x10\xbd\x19\xf6" //ru + "\x39\xde\xd0\xd2\x33\x8d\xb7\x2b\x81\x30\xef\x09\xcf\xba\x90\xbe\xce\xec\x71\xbf"},//mindgrind + {"sound/misc/water1.wav", FMOD_DM | FMOD_TF, 2, "\xc7\xdb\x25\x86\xe6\x0a\xf3\xba\x65\x39\xb5\xfd\xd6\xb9\x7e\x2d\x04\x93\xfd\xf9" //quake + "\x18\x03\x8a\x83\x3e\xe5\x05\x08\x65\x40\xbf\x6e\xa0\xb9\x5f\xaa\x07\xd8\xca\xe1"},//mindgrind + {"sound/misc/water2.wav", FMOD_DM | FMOD_TF, 2, "\xec\x75\xda\xcc\x80\xd7\x5c\xfb\x5b\x3b\xd7\xe2\xad\x60\x35\x9f\x85\x6b\x11\x4e" //quake + "\x17\x6b\x0a\xfb\x33\xbc\x5f\x06\xc5\x7c\xab\x5f\xaa\x01\x0c\x11\x2e\x61\x8b\x27"},//mindgrind + {"sound/misc/menu1.wav", FMOD_DM | FMOD_TF, 2, "\x17\xb8\x19\x2e\x5f\x1c\x0e\x0c\xf8\xec\xa0\xd7\x7e\xc2\x78\xb2\x3c\x92\xe1\xb0" //quake + "\x73\x63\x3f\xf5\x3c\x61\x35\xd6\xd4\x41\x67\x9f\x05\xc2\x5c\x9d\x25\xc4\x15\x96"},//sean + {"sound/misc/menu2.wav", FMOD_DM | FMOD_TF, 2, "\xb1\x9a\x71\xd6\x2b\x7e\x40\x0a\x3b\x05\x3a\xb0\xcb\xc2\x4b\x2a\xf3\x7f\xcd\x61" //quake + "\xeb\x31\x56\xca\x89\x71\x57\x7b\x4e\xb1\xfd\x58\x90\x51\x56\xc4\xc4\x36\xc9\x6f"},//sean + {"sound/misc/menu3.wav", FMOD_DM | FMOD_TF, 2, "\x9a\x0b\x12\xae\x2e\x7d\x21\x3a\x90\x09\x4f\xc3\xed\x32\x43\x2d\x76\x8f\xc1\x97" //quake + "\xfe\xa5\x0c\xe6\x49\xf9\x0c\x30\x09\x5c\x2f\xb7\x70\x1c\x6d\x2b\x1f\xff\x2f\xd9"},//sean + {"sound/misc/talk.wav", FMOD_DM | FMOD_TF, 2, "\x1c\x32\x15\x5b\x26\xd8\xf2\x1a\x9f\x8e\x22\xb0\x56\x6f\xc0\x49\x8b\x5e\x35\xa8" //quake + "\x66\x60\x59\xab\x9d\xd4\xe1\xc7\xab\x9a\xdf\x00\x07\xfc\x14\xbd\xee\xae\xb2\x4a"},//sean + {"sound/misc/basekey.wav", FMOD_DM | FMOD_TF, 2, "\xce\x34\x61\x92\xd3\x6d\x80\x22\x4a\x62\x52\x19\xe9\xf7\x43\x8f\x64\xfe\xfd\xa6" //quake + "\xd4\x71\x98\xc9\xef\x0f\x0e\xc6\xf9\x4b\xe6\x14\x34\x39\xf4\xba\x18\xdf\x76\x53"},//gpl + {"sound/doors/runeuse.wav", FMOD_DM | FMOD_TF, 2, "\x59\x6d\x1d\xf4\xe8\x93\x9b\xe9\x25\xfd\xed\x15\x9c\x49\x8a\x66\x72\x25\xc6\x1a" //quake + "\x00\xb9\x76\xc5\xb3\x88\x89\x8f\x72\x0d\xa1\x85\xec\x42\x31\x4f\xba\xe3\x91\xac"},//sean + {"gfx/colormap.lmp", FMOD_DM | FMOD_TF, 1, "\x95\x37\x3b\xa9\x97\x63\x01\x17\x38\x1d\x76\x0a\x7d\xe6\x66\x48\x75\x2a\x2a\x3c"},//quake + {"gfx/palette.lmp", FMOD_DM | FMOD_TF, 1, "\x42\xe2\xa2\xa6\xda\xf7\xd0\xba\x1f\x35\x63\xe2\xad\xf8\x51\x6d\x4a\x5d\xa4\x49"},//quake +}; +static enum { + FMOD_UNCHECKED, + FMOD_MODIFIED, + FMOD_UNMODIFIED, +} modifiles_status[countof(modifiles)]; +static qboolean modified_neednotify; +static double modified_timestamp; //so we don't spam reanouncements static void Validation_FilesModified (void) { - Con_Printf ("f_modified not implemented\n"); -} + size_t i; + unsigned int flagmatch = cl.teamfortress?FMOD_TF:FMOD_DM; + char buf[512]; + qboolean evilhaxxor = false; + modified_timestamp = 0; + modified_neednotify = true; + Q_strncpyz(buf, "modified:", sizeof(buf)); + for (i = 0; i < countof(modifiles); i++) + { + if ((modifiles[i].flags & flagmatch) && modifiles_status[i] == FMOD_MODIFIED) + { + evilhaxxor = true; + if (strlen(buf)>240) + { + modified_neednotify = false;//just spam at this point + Q_strncatz(buf, " & more...", sizeof(buf)); + break; + } + else + { + Q_strncatz(buf, " ", sizeof(buf)); + Q_strncatz(buf, COM_SkipPath(modifiles[i].name), sizeof(buf)); + } + } + } + if (!evilhaxxor) + Q_strncpyz(buf, "all models okay", sizeof(buf)); + + CL_SendClientCommand(true, "say %s", buf); +} +static qboolean Validation_IsModified(void) +{ + unsigned int flagmatch = cl.teamfortress?FMOD_TF:FMOD_DM; + size_t i; + for (i = 0; i < countof(modifiles); i++) + { + if ((modifiles[i].flags & flagmatch) && modifiles_status[i] == FMOD_MODIFIED) + return true; + } + modified_timestamp = 0; + modified_neednotify = true; + return false; +} +void Validation_WarnModified(void *ctx, void *data, size_t a, size_t b) +{ + if (modified_neednotify) + { + if (modified_timestamp && modified_timestamp > realtime) + return; + CL_SendClientCommand(true, "say warning: stuff changed! Previous f_modified response is no longer valid."); + modified_timestamp = realtime+3.0; //mute further anouncements for a while. + } +} +#endif +void Validation_FileLoaded(const char *filename, const qbyte *filedata, size_t filesize) +{ //usually called on worker threads +#ifdef HAVE_LEGACY + qbyte digest[20]; + size_t i, j; + unsigned int status; + for (i = 0; i < countof(modifiles); i++) + { + if (!strcmp(filename, modifiles[i].name) && (modifiles[i].flags & (cl.teamfortress?FMOD_TF:FMOD_DM))) + { + SHA1(digest, sizeof(digest), filedata, filesize); + + for (j = 0; j < modifiles[i].hashes; j++) + { + if (!memcmp(digest, modifiles[i].hash+j*20, 20)) + break; + } + status = (j==modifiles[i].hashes)?FMOD_MODIFIED:FMOD_UNMODIFIED; + if (status == FMOD_MODIFIED && modifiles_status[i] == FMOD_UNMODIFIED) + COM_AddWork(WG_MAIN, Validation_WarnModified, NULL, NULL, 0, 0); //make sure its the main thread that does the anouncing. + modifiles_status[i] = status; + return; + } + } +#endif +} void Validation_FlushFileList(void) { - f_modified_t *fm; - while(f_modified_list) - { - fm = f_modified_list->next; - - Z_Free(f_modified_list); - f_modified_list = fm; - } +#ifdef HAVE_LEGACY + //just wipe the lot, resetting it back to 'unchecked' + memset(modifiles_status, 0, sizeof(modifiles_status)); + modified_neednotify = false; + modified_timestamp = 0; +#endif } ///////////////////////// @@ -327,6 +579,7 @@ static void Validation_Scripts(void) Cbuf_AddText("say scripts are capped\n", RESTRICT_LOCAL); } +#ifdef HAVE_LEGACY static void Validation_FakeShaft(void) { extern cvar_t cl_truelightning; @@ -339,6 +592,7 @@ static void Validation_FakeShaft(void) else Cbuf_AddText("say fakeshaft off\n", RESTRICT_LOCAL); } +#endif static void Validation_System(void) { //subset of ruleset @@ -513,11 +767,16 @@ static void Validation_OldRuleset(void) static void Validation_AllChecks(void) { + extern cvar_t cl_iDrive; char servername[22]; char playername[16]; - char *enginebuild = version_string(); + char *rawenginebuild = version_string(); + char enginebuild[64]; char localpnamelen = strlen(cl.players[cl.playerview[0].playernum].name); char ruleset[1024]; + char extras[2][8]; + size_t i, j; + qboolean hadspace; //figure out the padding for the player's name. if (localpnamelen >= 15) @@ -529,15 +788,48 @@ static void Validation_AllChecks(void) playername[15-localpnamelen] = 0; } + for (i = 0, j = 0, hadspace=false; rawenginebuild[i]&& j+1string; + if (*line) + v_skyroom_set = true; + else + v_skyroom_set = false; + line = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL); + v_skyroom_origin[0] = atof(tok); + line = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL); + v_skyroom_origin[1] = atof(tok); + line = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL); + v_skyroom_origin[2] = atof(tok); + line = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL); + v_skyroom_origin[3] = atof(tok); + + line = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL); + v_skyroom_orientation[3] = atof(tok); + line = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL); + v_skyroom_orientation[0] = atof(tok); + line = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL); + v_skyroom_orientation[1] = atof(tok); + line = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL); + v_skyroom_orientation[2] = atof(tok); +} +cvar_t v_skyroom = CVARFCD("skyroom", "", CVAR_SEMICHEAT, V_Skyroom_Changed, "Specifies the center position of the skyroom's view. Skyrooms are drawn instead of skyboxes (typically with their own skybox around them). Entities in skyrooms will be drawn only when r_ignoreentpvs is 0. Can also be set with the _skyroom worldspawn key. This is overriden by csqc's VF_SKYROOM_CAMERA."); static cvar_t gl_cshiftpercent = CVAR("gl_cshiftpercent", "100"); cvar_t gl_cshiftenabled = CVARFD("gl_polyblend", "1", CVAR_ARCHIVE, "Controls whether temporary whole-screen colour changes should be honoured or not. Change gl_cshiftpercent if you want to adjust the intensity.\nThis does not affect v_cshift commands sent from the server."); @@ -349,7 +377,7 @@ static cshift_t cshift_lava = { {255,80,0}, 150 }; //static cshift_t cshift_server = { {130,80,50}, 0 }; -cvar_t v_gamma = CVARFCD("gamma", "1.0", CVAR_ARCHIVE|CVAR_RENDERERCALLBACK, V_Gamma_Callback, "Controls how bright the screen is. Setting this to anything but 1 without hardware gamma requires glsl support and can noticably harm your framerate."); +cvar_t v_gamma = CVARAFCD("gamma", "1.0", "v_gamma", CVAR_ARCHIVE|CVAR_RENDERERCALLBACK, V_Gamma_Callback, "Controls how bright the screen is. Setting this to anything but 1 without hardware gamma requires glsl support and can noticably harm your framerate."); cvar_t v_gammainverted = CVARFCD("v_gammainverted", "0", CVAR_ARCHIVE, V_Gamma_Callback, "Boolean that controls whether the gamma should be inverted (like quake) or not."); cvar_t v_contrast = CVARAFCD("contrast", "1.0", "v_contrast", CVAR_ARCHIVE, V_Gamma_Callback, "Scales colour values linearly to make your screen easier to see. Setting this to anything but 1 without hardware gamma will reduce your framerates a little."); cvar_t v_contrastboost = CVARFCD("v_contrastboost", "1.0", CVAR_ARCHIVE, V_Gamma_Callback, "Amplifies contrast in dark areas"); @@ -1619,12 +1647,12 @@ void V_CalcRefdef (playerview_t *pv) VectorMA(r_refdef.vieworg, v_gunkick.value, pv->punchorigin, r_refdef.vieworg); } - if (*v_skyroom_origin.string) + if (v_skyroom_set) { r_refdef.skyroom_enabled = true; - r_refdef.skyroom_pos[0] = v_skyroom_origin.vec4[0]; - r_refdef.skyroom_pos[1] = v_skyroom_origin.vec4[1]; - r_refdef.skyroom_pos[2] = v_skyroom_origin.vec4[2]; + VectorMA(v_skyroom_origin, v_skyroom_origin[3], r_refdef.vieworg, r_refdef.skyroom_pos); + Vector4Copy(v_skyroom_orientation, r_refdef.skyroom_spin); + r_refdef.skyroom_spin[3] *= cl.time; } if (chase_active.ival && cls.allow_cheats) //cheat restriction might be lifted some time when any wallhacks are solved. @@ -2577,14 +2605,14 @@ void V_Init (void) Cvar_Register (&cl_bobmodel_side, VIEWVARS); Cvar_Register (&cl_bobmodel_up, VIEWVARS); Cvar_Register (&cl_bobmodel_speed, VIEWVARS); + Cvar_Register (&v_viewmodel_quake, VIEWVARS); Cvar_Register (&v_kicktime, VIEWVARS); Cvar_Register (&v_kickroll, VIEWVARS); Cvar_Register (&v_kickpitch, VIEWVARS); Cvar_Register (&v_deathtilt, VIEWVARS); - Cvar_Register (&v_skyroom_origin, VIEWVARS); - Cvar_Register (&v_skyroom_orientation, VIEWVARS); + Cvar_Register (&v_skyroom, VIEWVARS); Cvar_Register (&scr_autoid, VIEWVARS); Cvar_Register (&scr_autoid_team, VIEWVARS); diff --git a/engine/client/wad.c b/engine/client/wad.c index 259dda63..b2e3351e 100644 --- a/engine/client/wad.c +++ b/engine/client/wad.c @@ -895,8 +895,8 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in } else if (!strcmp("skyroom", key)) // for Quake mappers that lack the proper tools { - extern cvar_t v_skyroom_origin; - Cvar_LockFromServer(&v_skyroom_origin, token); + extern cvar_t v_skyroom; + Cvar_LockFromServer(&v_skyroom, token); } else if (!strcmp("skyname", key)) // for HalfLife maps { diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index 85df32a4..1afbc778 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -71,6 +71,8 @@ qboolean suppress; static void QDECL TP_SkinCvar_Callback(struct cvar_s *var, char *oldvalue); static void QDECL TP_TeamColor_CB (struct cvar_s *var, char *oldvalue); static void QDECL TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue); +//cvar_t enemyforceskins = CVARD("enemyforceskins", "0", "0: Read the skin as-is.\n1: Use the player's name instead of their skin setting.\n2: Use their userid (probably too unreliable).3: Use a per-team player index.\n"); +//cvar_t teamforceskins = CVARD("enemyforceskins", "0", "0: Read the skin as-is.\n1: Use the player's name instead of their skin setting.\n2: Use their userid (probably too unreliable).3: Use a per-team player index.\n"); #define TP_SKIN_CVARS \ TP_CVARAC(cl_teamskin, "", teamskin, TP_SkinCvar_Callback); \ TP_CVARAC(cl_enemyskin, "", enemyskin, TP_SkinCvar_Callback); \ diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 5767ad00..85e1668c 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -177,7 +177,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define FULLENGINENAME "FTE Quake" //the posh name for the engine #endif #ifndef ENGINEWEBSITE - #define ENGINEWEBSITE "^8http://^4fte.triptohell.info" //url for program + #define ENGINEWEBSITE "^8http://^4fte^8.^4triptohell^8.^4info" //url for program #endif #if !defined(_WIN32) || defined(WINRT) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 9de8c89b..21ed5941 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -666,54 +666,57 @@ static void Cmd_Exec_f (void) { f = Cmd_Argv(1); if (!*f) - f = "fte"; + f = fs_manifest->mainconfig; + if (!*f) + f = "config"; snprintf(name, sizeof(name)-5, "configs/%s", f); COM_DefaultExtension(name, ".cfg", sizeof(name)); } else { Q_strncpyz(name, Cmd_Argv(1), sizeof(name)); -#ifndef QUAKETC - //fte writes to a different config file from that specified by the quake.rc, to avoid conflicts. - //so make sure that fte's settings override those from whatever other engine that wrote the legacy config.cfg file. - if (!strcmp(name, "config.cfg") || !strcmp(name, "q3config.cfg")) + if (*fs_manifest->mainconfig && Q_strcasecmp("config.cfg", fs_manifest->mainconfig) && Q_strcasecmp("q3config.cfg", fs_manifest->mainconfig)) { - int cfgdepth = COM_FDepthFile(name, true); - int defdepth = COM_FDepthFile("default.cfg", true); - Cbuf_InsertText("exec fte.cfg", Cmd_ExecLevel, true); - if (defdepth < cfgdepth && cfgdepth != FDEPTH_MISSING) + //fte writes to a different config file from that specified by the quake.rc, to avoid conflicts. + //so make sure that fte's settings override those from whatever other engine that wrote the legacy config.cfg file. + if (!strcmp(name, "config.cfg") || !strcmp(name, "q3config.cfg")) { - if (cl_warncmd.ival) + int cfgdepth = COM_FDepthFile(name, true); + int defdepth = COM_FDepthFile("default.cfg", true); + Cbuf_InsertText(va("exec %s", fs_manifest->mainconfig), Cmd_ExecLevel, true); + if (defdepth < cfgdepth && cfgdepth != FDEPTH_MISSING) { - char fulldefault[MAX_OSPATH]; - char fullconfig[MAX_OSPATH]; - *fulldefault = *fullconfig = 0; - FS_NativePath("default.cfg", FS_GAME, fulldefault, sizeof(fulldefault)); - FS_NativePath(name, FS_GAME, fullconfig, sizeof(fullconfig)); - Con_Printf("Refusing to execute \"%s\", superceded by %s\n", fullconfig, fulldefault); + if (cl_warncmd.ival) + { + char fulldefault[MAX_OSPATH]; + char fullconfig[MAX_OSPATH]; + *fulldefault = *fullconfig = 0; + FS_NativePath("default.cfg", FS_GAME, fulldefault, sizeof(fulldefault)); + FS_NativePath(name, FS_GAME, fullconfig, sizeof(fullconfig)); + Con_Printf("Refusing to execute \"%s\", superceded by %s\n", fullconfig, fulldefault); + } + return; + } + } + else if (!strcmp(name, fs_manifest->mainconfig)) + { + int cfgdepth = COM_FDepthFile(name, true); + int defdepth = COM_FDepthFile("default.cfg", true); + if (defdepth < cfgdepth && cfgdepth != FDEPTH_MISSING) + { + if (cl_warncmd.ival) + { + char fulldefault[MAX_OSPATH]; + char fullconfig[MAX_OSPATH]; + *fulldefault = *fullconfig = 0; + FS_NativePath("default.cfg", FS_GAME, fulldefault, sizeof(fulldefault)); + FS_NativePath(name, FS_GAME, fullconfig, sizeof(fullconfig)); + Con_Printf("Refusing to execute \"%s\", superceded by %s\n", fullconfig, fulldefault); + } + return; } - return; } } - else if (!strcmp(name, "fte.cfg")) - { - int cfgdepth = COM_FDepthFile(name, true); - int defdepth = COM_FDepthFile("default.cfg", true); - if (defdepth < cfgdepth && cfgdepth != FDEPTH_MISSING) - { - if (cl_warncmd.ival) - { - char fulldefault[MAX_OSPATH]; - char fullconfig[MAX_OSPATH]; - *fulldefault = *fullconfig = 0; - FS_NativePath("default.cfg", FS_GAME, fulldefault, sizeof(fulldefault)); - FS_NativePath(name, FS_GAME, fullconfig, sizeof(fullconfig)); - Con_Printf("Refusing to execute \"%s\", superceded by %s\n", fullconfig, fulldefault); - } - return; - } - } -#endif } if (!strncmp(name, "../", 3) || !strncmp(name, "..\\", 3) || !strncmp(name, "./", 2) || !strncmp(name, ".\\", 2)) @@ -760,7 +763,7 @@ static void Cmd_Exec_f (void) s+=3; } - if (!strcmp(name, "config.cfg") || !strcmp(name, "q3config.cfg") || !strcmp(name, "fte.cfg")) + if (!strcmp(name, "config.cfg") || !strcmp(name, "q3config.cfg") || !strcmp(name, fs_manifest->mainconfig)) { //if the config is from id1 and the default.cfg was from some mod, make sure the default.cfg overrides the config. //we won't just exec the default instead, because we can at least retain things which are not specified (ie: a few binds) @@ -1352,6 +1355,7 @@ void Alias_WipeStuffedAliases(void) void Cvar_List_f (void); void Cvar_Reset_f (void); +void Cvar_ResetAll_f(void); void Cvar_LockDefaults_f(void); void Cvar_PurgeDefaults_f(void); @@ -2694,7 +2698,7 @@ static void Cmd_ExecuteStringGlobalsAreEvil (const char *text, int level) if (dpcompat_console.ival && !strncmp(text, "alias", 5) && (text[5] == ' ' || text[5] == '\t')) ; //certain commands don't get pre-expanded in dp. evil hack. quote them to pre-expand anyway. double evil. else - text = Cmd_ExpandString(text, dest, sizeof(dest), &level, !Cmd_IsInsecure()?true:false, true); + text = Cmd_ExpandString(text, dest, sizeof(dest), &level, true/*!Cmd_IsInsecure()?true:false*/, true); Cmd_TokenizeString (text, (level == RESTRICT_LOCAL&&!dpcompat_console.ival)?true:false, false); // execute the command line @@ -3950,20 +3954,10 @@ static void Cmd_WriteConfig_f(void) char sysname[MAX_OSPATH]; qboolean all = true; - if (Cmd_IsInsecure() && Cmd_Argc() > 1) - { - Con_Printf ("%s not allowed\n", Cmd_Argv(0)); - return; - } - filename = Cmd_Argv(1); if (!*filename) { -#ifdef QUAKETC - snprintf(fname, sizeof(fname), "config.cfg"); -#else - snprintf(fname, sizeof(fname), "fte.cfg"); -#endif + Q_strncpyz(fname, fs_manifest->mainconfig, sizeof(fname)); #if defined(CL_MASTER) && defined(HAVE_CLIENT) MasterInfo_WriteServers(); @@ -3973,8 +3967,32 @@ static void Cmd_WriteConfig_f(void) all = cfg_save_all.ival; } + else if (!Q_strcasecmp(Cmd_Argv(0), "saveconfig")) + { + //dpcompat: this variation allows writing to any path. at least force the extension. + snprintf(fname, sizeof(fname), "%s", filename); + COM_RequireExtension(fname, ".cfg", sizeof(fname)); + + if (Cmd_IsInsecure() && strncmp(fname, "data/", 5)) + { + Con_Printf ("%s %s: not allowed\n", Cmd_Argv(0), Cmd_Args()); + return; + } + + FS_NativePath(fname, FS_BASEGAMEONLY, sysname, sizeof(sysname)); + FS_CreatePath(fname, FS_BASEGAMEONLY); + f = FS_OpenVFS(fname, "wbp", FS_BASEGAMEONLY); + + all = cfg_save_all.ival;// || !*cfg_save_all.string; + } else { + if (Cmd_IsInsecure()) + { + Con_Printf ("%s %s: not allowed\n", Cmd_Argv(0), Cmd_Args()); + return; + } + if (strstr(filename, "..")) { Con_Printf (CON_ERROR "Couldn't write config %s\n",filename); @@ -4212,6 +4230,7 @@ void Cmd_Init (void) // register our commands // Cmd_AddCommandAD ("cfg_save",Cmd_WriteConfig_f, Cmd_Exec_c, NULL); + Cmd_AddCommandAD ("saveconfig",Cmd_WriteConfig_f, Cmd_Exec_c, NULL); //for dpcompat Cmd_AddCommandAD ("cfg_load",Cmd_Exec_f, Cmd_Exec_c, NULL); Cmd_AddCommand ("cfg_reset",Cmd_Reset_f); @@ -4256,6 +4275,9 @@ void Cmd_Init (void) Cmd_AddCommandD ("cvarwatch", Cvar_Watch_f, "Prints a notification when the named cvar is changed. Also displays the start/end of configs. Alternatively, use '-watch foo' on the commandline."); Cmd_AddCommand ("cvar_lockdefaults", Cvar_LockDefaults_f); Cmd_AddCommandD ("cvar_purgedefaults", Cvar_PurgeDefaults_f, "Resets all cvar defaults to back to the engine's default. Does not change their active value."); + Cmd_AddCommandD ("cvar_resettodefaults_nosaveonly", Cvar_ResetAll_f, "Resets all unsaved cvars to the mod's default value."); + Cmd_AddCommandD ("cvar_resettodefaults_saveonly", Cvar_ResetAll_f, "Resets all saved cvars to the mod's default value (without changing unsaved cvars)."); + Cmd_AddCommandD ("cvar_resettodefaults_all", Cvar_ResetAll_f, "Resets all cvars to the mod's default value."); Cmd_AddCommandD ("apropos", Cmd_Apropos_f, "Lists all cvars or commands with the specified substring somewhere in their name or descrition."); Cmd_AddCommandD ("find", Cmd_Apropos_f, "Lists all cvars or commands with the specified substring somewhere in their name or descrition."); diff --git a/engine/common/cmd.h b/engine/common/cmd.h index c1768745..e1f3ebd1 100644 --- a/engine/common/cmd.h +++ b/engine/common/cmd.h @@ -22,6 +22,31 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //=========================================================================== +/*FIXME: rewrite this to use something like the following +typedef struct +{ + qbyte level:8; + qbyte seat:4; //for splitscreen binds etc + qbyte pad:2; + qbyte insecure:1; //from gamecode, untrusted configs, stuffed binds, etc. flagged for many reasons. + qbyte cvarlatch:1; //latches cvars so the user cannot change them till next map. should be RESTRICT_LOCAL to avoid users cheating by restrict-to-block server commands. +} cmdpermissions_t; +typedef struct +{ + cbuf_t *cbuf; //exec etc inserts into this + cmdpermissions_t p; //access rights +} cmdstate_t; +void Cbuf_AddText(const char *text, qboolean addnl, qboolean insert, const cmdstate_t *cstate); //null for local? +char *Cbuf_GetNext(cmdpermissions_t permissions, qboolean ignoresemicolon); +void Cbuf_Execute(cbuf_t *cbuf); +void Cmd_ExecuteString (const char *text, const cmdstate_t *cstate); + +//rcon can use a private cbuf, allowing it to parse properly despite level changes +//cbuf must internally track cmdpermissions_t of different blocks. inserstions/additions may merge, others force \n termination (network can do its own buffering). + +typedef void (*xcommand_t) (const cmdstate_t *cstate); +*/ + /* Any number of commands can be added in a frame, from several different sources. @@ -168,11 +193,11 @@ void Cmd_Args_Set(const char *newargs, size_t len); #define RESTRICT_MAX RESTRICT_MAX_USER -#define RESTRICT_LOCAL RESTRICT_MAX -#define RESTRICT_INSECURE RESTRICT_MAX+1 -#define RESTRICT_SERVER RESTRICT_MAX+2 +#define RESTRICT_LOCAL RESTRICT_MAX //commands typed at the console +#define RESTRICT_INSECURE RESTRICT_MAX+1 //commands from csqc or untrusted sources (really should be a separate flag, requires cbuf rewrite) +#define RESTRICT_SERVER RESTRICT_MAX+2 //commands from ssqc (untrusted, but allowed to lock cvars) #define RESTRICT_RCON rcon_level.ival -#define RESTRICT_PROGS RESTRICT_MAX-2 +//#define RESTRICT_SSQC RESTRICT_MAX-2 #define Cmd_FromGamecode() (Cmd_ExecLevel>=RESTRICT_SERVER) //cheat provention. block cheats if its not fromgamecode #define Cmd_IsInsecure() (Cmd_ExecLevel>=RESTRICT_INSECURE) //prevention from the server from breaking/crashing/wiping us. if this returns true, block file access etc. diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 8188e2ef..626eb6d0 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -34,6 +34,7 @@ cvar_t r_meshpitch = CVARCD ("r_meshpitch", "1", r_meshpitch_callback, "Sp #else cvar_t r_meshpitch = CVARCD ("r_meshpitch", "-1", r_meshpitch_callback, "Specifies the direction of the pitch angle on mesh models formats, Quake compatibility requires -1."); #endif +cvar_t dpcompat_skinfiles = CVARD ("dpcompat_skinfiles", "0", "When set, uses a nodraw shader for any unmentioned surfaces."); #ifdef HAVE_CLIENT static void Mod_UpdateCRC(void *ctx, void *data, size_t a, size_t b) @@ -71,6 +72,7 @@ void Mod_DoCRC(model_t *mod, char *buffer, int buffersize) mod->tainted = (crc != 6967); } } + Validation_FileLoaded(mod->publicname, buffer, buffersize); #endif } @@ -3170,7 +3172,7 @@ void Mod_LoadAliasShaders(model_t *mod) { if (!f->defaultshader) { - if (ai->csurface.flags & 0x80) //nodraw + if ((ai->csurface.flags & 0x80) || dpcompat_skinfiles.ival) //nodraw f->shader = R_RegisterShader(f->shadername, SUF_NONE, "{\nsurfaceparm nodraw\nsurfaceparm nodlight\nsurfaceparm nomarks\nsurfaceparm noshadows\n}\n"); else f->shader = R_RegisterSkin(f->shadername, mod->name); @@ -7207,7 +7209,7 @@ static qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t mesh = (dpmmesh_t*)((char*)buffer + header->ofs_meshs); for (i = 0; i < header->num_meshs; i++, mesh++) { - m = &root[i]; + m = &root[i]; Mod_DefaultMesh(m, mesh->shadername, i); if (i < header->num_meshs-1) m->nextsurf = &root[i+1]; @@ -9529,4 +9531,8 @@ void Alias_Register(void) #ifdef MODELFMT_OBJ Mod_RegisterModelFormatText(NULL, "Wavefront Object (obj)", ".obj", Mod_LoadObjModel); #endif + +#ifndef SERVERONLY + Cvar_Register(&dpcompat_skinfiles, NULL); +#endif } diff --git a/engine/common/common.c b/engine/common/common.c index 078599bc..aa779694 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -104,7 +104,7 @@ cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "A cvar_t com_nogamedirnativecode = CVARFD("com_nogamedirnativecode", "1", CVAR_NOTFROMSERVER, FULLENGINENAME" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger delayed eremote exploits in any engine (including "DISTRIBUTION") which is later run from the same gamedir.\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 0, as well as ensure that you don't run unsafe clients."); cvar_t sys_platform = CVAR("sys_platform", PLATFORM); cvar_t pkg_downloads_url = CVARFD("pkg_downloads_url", NULL, CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET, "The URL of a package updates list."); //read from the default.fmf -cvar_t pkg_autoupdate = CVARFD("pkg_autoupdate", "1", CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET, "Controls autoupdates, can only be changed via the downloads menu.\n0: off.\n1: enabled (stable only).\n2: enabled (unstable).\nNote that autoupdate will still prompt the user to actually apply the changes."); //read from the package list only. +cvar_t pkg_autoupdate = CVARFD("pkg_autoupdate", "-1", CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET, "Controls autoupdates, can only be changed via the downloads menu.\n0: off.\n1: enabled (stable only).\n2: enabled (unstable).\nNote that autoupdate will still prompt the user to actually apply the changes."); //read from the package list only. #ifdef HAVE_LEGACY cvar_t pm_noround = CVARD("pm_noround", "0", "Disables player prediction snapping, in a way that cannot be reliably predicted but may be needed to avoid map bugs."); #endif @@ -4785,11 +4785,15 @@ static void COM_Version_f (void) Con_Printf("^4"ENGINEWEBSITE"\n"); Con_Printf("%s\n", version_string()); +#if defined(SVNREVISION) && defined(SVNDATE) + Con_Printf("SVN Revision: %s - %s\n",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE)); +#else Con_TPrintf ("Exe: %s %s\n", __DATE__, __TIME__); #ifdef SVNREVISION if (strcmp(STRINGIFY(SVNREVISION), "-")) Con_Printf("SVN Revision: %s\n",STRINGIFY(SVNREVISION)); #endif +#endif #ifdef CONFIG_FILE_NAME Con_Printf("Build config: %s\n\n", COM_SkipPath(STRINGIFY(CONFIG_FILE_NAME))); #endif @@ -7503,12 +7507,14 @@ char *version_string(void) { #ifdef OFFICIAL_RELEASE Q_snprintfz(s, sizeof(s), "%s v%i.%02i", DISTRIBUTION, FTE_VER_MAJOR, FTE_VER_MINOR); +#elif defined(SVNREVISION) && defined(SVNDATE) + Q_snprintfz(s, sizeof(s), "%s SVN %s", DISTRIBUTION, STRINGIFY(SVNREVISION)); //if both are defined then its a known unmodified svn revision. #else -#if defined(SVNREVISION) + #if defined(SVNREVISION) if (strcmp(STRINGIFY(SVNREVISION), "-")) - Q_snprintfz(s, sizeof(s), "%s SVN %s", DISTRIBUTION, STRINGIFY(SVNREVISION)); + Q_snprintfz(s, sizeof(s), "%s SVN %s %s", DISTRIBUTION, STRINGIFY(SVNREVISION), __DATE__); else -#endif + #endif Q_snprintfz(s, sizeof(s), "%s build %s", DISTRIBUTION, __DATE__); #endif done = true; diff --git a/engine/common/common.h b/engine/common/common.h index 0a29e61c..4e17ac8f 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -418,7 +418,7 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q #endif #define PFS_CENTERED 16 //flag used by console prints (text should remain centered) #define PFS_NONOTIFY 32 //flag used by console prints (text won't be visible other than by looking at the console) -conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t *out, int outsize, int keepmarkup); //ext is usually CON_WHITEMASK, returns its null terminator +conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t *out, int outsize_bytes, int keepmarkup); //ext is usually CON_WHITEMASK, returns its null terminator unsigned int utf8_decode(int *error, const void *in, char const**out); unsigned int utf8_encode(void *out, unsigned int unicode, int maxlen); unsigned int iso88591_encode(char *out, unsigned int unicode, int maxlen, qboolean markup); @@ -497,7 +497,7 @@ typedef struct searchpath_s { searchpathfuncs_t *handle; - unsigned int flags; + unsigned int flags; //SPF_* char logicalpath[MAX_OSPATH]; //printable hunam-readable location of the package. generally includes a system path, including nested packages. char purepath[256]; //server tracks the path used to load them so it can tell the client @@ -668,7 +668,13 @@ typedef struct } parsever; int minver; //if the engine svn revision is lower than this, the manifest will not be used as an 'upgrade'. int maxver; //if not 0, the manifest will not be used - qboolean disablehomedir; + enum + { + MANIFEST_HOMEDIRWHENREADONLY=0, + MANIFEST_NOHOMEDIR, + MANIFEST_FORCEHOMEDIR, + } homedirtype; + char *mainconfig; //eg "fte.cfg", reducing conflicts with other engines, but can be other values... char *updateurl; //url to download an updated manifest file from. char *updatefile; //this is the file that needs to be written to update the manifest. char *installation; //optional hardcoded commercial name, used for scanning the registry to find existing installs. @@ -683,7 +689,17 @@ typedef struct char *basedir; //this is where we expect to find the data. struct { - qboolean base; + enum + { + GAMEDIR_DEFAULTFLAGS=0, //forgotten on gamedir switches (and a higher priority) + GAMEDIR_BASEGAME=1u<<0, //not forgotten on gamedir switches (and a lower priority) + GAMEDIR_PRIVATE=1u<<1, //don't report as the gamedir on networks. + GAMEDIR_READONLY=1u<<2, //don't write here... + GAMEDIR_USEBASEDIR=1u<<3, //packages will be read from the basedir (and homedir), but not other files. path is an empty string. + GAMEDIR_STEAMGAME=1u<<4, //finds the game via steam. must also be private+readonly. + + GAMEDIR_SPECIAL=GAMEDIR_USEBASEDIR|GAMEDIR_STEAMGAME, //if one of these flags, then the gamedir cannot be simply concatenated to the basedir/homedir. + } flags; char *path; } gamepath[8]; struct manpack_s @@ -709,6 +725,7 @@ void COM_InitFilesystem (void); //does not set up any gamedirs. qboolean FS_DownloadingPackage(void); void FS_CreateBasedir(const char *path); qboolean FS_ChangeGame(ftemanifest_t *newgame, qboolean allowreloadconfigs, qboolean allowbasedirchange); +qboolean FS_GameIsInitialised(void); void FS_Shutdown(void); struct gamepacks { diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 27e748f0..4dddb59d 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -183,6 +183,7 @@ void Cvar_List_f (void) char *var, *search, *gsearch; int gnum, i, num = 0; int listflags = 0, cvarflags = 0; + int total = 0; char strtmp[512]; static char *cvarlist_help = "cvarlist list all cvars matching given parameters\n" @@ -380,6 +381,7 @@ showhelp: // print cvar name Con_Printf("%s", cmd->name); + total++; // print current value if (listflags & CLF_VALUES) @@ -428,6 +430,9 @@ showhelp: if (!(listflags & CLF_RAW) && gnum) Con_Printf("\n"); } + + if (search && !strcmp(search, "*")) + Con_Printf("%i cvars\n", total); } //default values are meant to be constants. @@ -487,6 +492,38 @@ void Cvar_PurgeDefaults_f(void) } } +void Cvar_ResetAll_f(void) +{ + cvar_group_t *grp; + cvar_t *var; + unsigned int bitmask; //the bits to care about + unsigned int bitmaskvalue; //must match this value + char *cmd = Cmd_Argv(0); + if (!Q_strcasecmp(cmd, "cvar_resettodefaults_saveonly")) + bitmask = CVAR_NORESET|CVAR_NOSET|CVAR_SAVE, bitmaskvalue = CVAR_SAVE; + else if (!Q_strcasecmp(cmd, "cvar_resettodefaults_nosaveonly")) + bitmask = CVAR_NORESET|CVAR_NOSET|CVAR_SAVE, bitmaskvalue = 0; + else //others... + bitmask = CVAR_NORESET|CVAR_NOSET, bitmaskvalue = 0; + + for (grp=cvar_groups ; grp ; grp=grp->next) + { + for (var=grp->cvars ; var ; var=var->next) + { + if (!var->enginevalue) + continue; //can't reset the cvar's default if its an engine cvar. + if ((var->flags & bitmask) != bitmaskvalue) + continue; + + if (var->defaultstr != var->enginevalue) + { + Cvar_DefaultFree(var->defaultstr); + var->defaultstr = var->enginevalue; + } + } + } +} + #define CRF_ALTNAME 0x1 void Cvar_Reset_f (void) { diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 0dba5eaa..3bd4846f 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -123,7 +123,7 @@ typedef struct cvar_group_s //freestyle #define CVAR_POINTER (1<<5) // q2 style. May be converted to q1 if needed. These are often specified on the command line and then converted into q1 when registered properly. -#define CVAR_UNUSED (1<<6) //the default string was malloced/needs to be malloced, free on unregister +//#define CVAR_UNUSED (1<<6) //the default string was malloced/needs to be malloced, free on unregister #define CVAR_NOTFROMSERVER (1<<7) //cvar cannot be set by gamecode. the console will ignore changes to cvars if set at from the server or any gamecode. This is to protect against security flaws - like qterm #define CVAR_USERCREATED (1<<8) //write a 'set' or 'seta' in front of the var name. #define CVAR_CHEAT (1<<9) //latch to the default, unless cheats are enabled. diff --git a/engine/common/fs.c b/engine/common/fs.c index 8673ae3b..5f363d44 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -34,10 +34,12 @@ cvar_t cfg_reload_on_gamedir = CVAR("cfg_reload_on_gamedir", "1"); cvar_t fs_game = CVARFCD("game", "", CVAR_NOSAVE|CVAR_NORESET, fs_game_callback, "Provided for Q2 compat."); cvar_t fs_gamedir = CVARFD("fs_gamedir", "", CVAR_NOUNSAFEEXPAND|CVAR_NOSET|CVAR_NOSAVE, "Provided for Q2 compat."); cvar_t fs_basedir = CVARFD("fs_basedir", "", CVAR_NOUNSAFEEXPAND|CVAR_NOSET|CVAR_NOSAVE, "Provided for Q2 compat."); +cvar_t dpcompat_ignoremodificationtimes = CVARAFD("fs_packageprioritisation", "1", "dpcompat_ignoremodificationtimes", CVAR_NOUNSAFEEXPAND|CVAR_NOSAVE, "Favours the package that is:\n0: Most recently modified\n1: Is alphabetically last (favour z over a, 9 over 0)."); int active_fs_cachetype; static int fs_referencetype; int fs_finds; void COM_CheckRegistered (void); +static qboolean Sys_SteamHasFile(char *basepath, int basepathlen, char *steamdir, char *fname); static void QDECL fs_game_callback(cvar_t *var, char *oldvalue) { @@ -264,13 +266,13 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm) newm->rtcbroker = Z_StrDup(oldm->rtcbroker); if (oldm->basedir) newm->basedir = Z_StrDup(oldm->basedir); - newm->disablehomedir = oldm->disablehomedir; + newm->homedirtype = oldm->homedirtype; for (i = 0; i < sizeof(newm->gamepath) / sizeof(newm->gamepath[0]); i++) { if (oldm->gamepath[i].path) newm->gamepath[i].path = Z_StrDup(oldm->gamepath[i].path); - newm->gamepath[i].base = oldm->gamepath[i].base; + newm->gamepath[i].flags = oldm->gamepath[i].flags; } for (i = 0; i < sizeof(newm->package) / sizeof(newm->package[0]); i++) { @@ -319,10 +321,16 @@ static void FS_Manifest_Print(ftemanifest_t *man) { if (man->gamepath[i].path) { - if (man->gamepath[i].base) - Con_Printf("basegame %s\n", COM_QuotedString(man->gamepath[i].path, buffer, sizeof(buffer), false)); + size_t bufsize = strlen(man->gamepath[i].path) + 16; + char *str = Z_Malloc(bufsize); + if (man->gamepath[i].flags & GAMEDIR_PRIVATE) + Q_strncatz(str, "*", bufsize); + Q_strncatz(str, man->gamepath[i].path, bufsize); + + if (man->gamepath[i].flags & GAMEDIR_BASEGAME) + Con_Printf("basegame %s\n", COM_QuotedString(str, buffer, sizeof(buffer), false)); else - Con_Printf("gamedir %s\n", COM_QuotedString(man->gamepath[i].path, buffer, sizeof(buffer), false)); + Con_Printf("gamedir %s\n", COM_QuotedString(str, buffer, sizeof(buffer), false)); } } @@ -355,7 +363,7 @@ static void FS_Manifest_PurgeGamedirs(ftemanifest_t *man) int i; for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++) { - if (man->gamepath[i].path && !man->gamepath[i].base) + if (man->gamepath[i].path && !(man->gamepath[i].flags&GAMEDIR_BASEGAME)) { Z_Free(man->gamepath[i].path); man->gamepath[i].path = NULL; @@ -378,6 +386,12 @@ static ftemanifest_t *FS_Manifest_Create(const char *syspath) if (syspath) man->updatefile = Z_StrDup(syspath); //this should be a system path. + +#ifdef QUAKETC + man->mainconfig = Z_StrDup("config.cfg"); +#else + man->mainconfig = Z_StrDup("fte.cfg"); +#endif return man; } @@ -494,6 +508,33 @@ mirror: return false; } +static qboolean FS_GamedirIsOkay(const char *path) +{ + if (!*path || strchr(path, '\n') || strchr(path, '\r') || !strcmp(path, ".") || !strcmp(path, "..") || strchr(path, ':') || strchr(path, '/') || strchr(path, '\\') || strchr(path, '$')) + { + Con_Printf("Illegal path specified: %s\n", path); + return false; + } + + //don't allow leading dots, hidden files are evil. + //don't allow complex paths. those are evil too. + if (!*path || *path == '.' || !strcmp(path, ".") || strstr(path, "..") || strstr(path, "/") + || strstr(path, "\\") || strstr(path, ":") ) + { + Con_Printf ("Gamedir should be a single filename, not \"%s\"\n", path); + return false; + } + + //some gamedirs should never be used for actual games/mods. Reject them. + if (!Q_strncasecmp(path, "downloads", 9) || !Q_strncasecmp(path, "docs", 4) || !Q_strncasecmp(path, "help", 4)) + { + Con_Printf ("Gamedir should not be \"%s\"\n", path); + return false; + } + + return true; +} + //parse Cmd_Argv tokens into the manifest. static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man) { @@ -551,6 +592,11 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man) Z_Free(man->protocolname); man->protocolname = Z_StrDup(Cmd_Argv(1)); } + else if (!Q_strcasecmp(cmd, "mainconfig")) + { + Z_Free(man->mainconfig); + man->mainconfig = Z_StrDup(Cmd_Argv(1)); + } else if (!Q_strcasecmp(cmd, "defaultexec")) { Z_Free(man->defaultexec); @@ -574,35 +620,66 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man) Z_Free(man->updateurl); man->updateurl = Z_StrDup(Cmd_Argv(1)); } - else if (!Q_strcasecmp(cmd, "disablehomedir")) + else if (!Q_strcasecmp(cmd, "disablehomedir") || !Q_strcasecmp(cmd, "homedirmode")) { - man->disablehomedir = !!atoi(Cmd_Argv(1)); + char *arg = Cmd_Argv(1); + if (!Q_strcasecmp(arg, "auto")) + man->homedirtype = MANIFEST_HOMEDIRWHENREADONLY; + else if (!*arg || atoi(arg)) + man->homedirtype = MANIFEST_NOHOMEDIR; + else if (!atoi(arg) || !Q_strcasecmp(arg, "force")) + man->homedirtype = MANIFEST_FORCEHOMEDIR; + else if (!Q_strcasecmp(arg, "never")) + man->homedirtype = MANIFEST_NOHOMEDIR; } else if (!Q_strcasecmp(cmd, "basegame") || !Q_strcasecmp(cmd, "gamedir")) { int i; char *newdir = Cmd_Argv(1); - //reject various evil path arguments. - if (!*newdir || strchr(newdir, '\n') || strchr(newdir, '\r') || !strcmp(newdir, ".") || !strcmp(newdir, "..") || strchr(newdir, ':') || strchr(newdir, '/') || strchr(newdir, '\\') || strchr(newdir, '$')) + for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++) { - Con_Printf("Illegal path specified: %s\n", newdir); - } - else - { - for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++) + if (!man->gamepath[i].path) { - if (!man->gamepath[i].path) - { - man->gamepath[i].base = !Q_strcasecmp(cmd, "basegame"); + man->gamepath[i].flags = GAMEDIR_DEFAULTFLAGS; + if (!Q_strcasecmp(cmd, "basegame")) + man->gamepath[i].flags |= GAMEDIR_BASEGAME; + + if (*newdir == '*') + { //*dir makes the dir 'private' and not networked. + newdir++; + man->gamepath[i].flags |= GAMEDIR_PRIVATE; + if (!*newdir) + { //a single asterisk means load packages from the basedir (but not other files). This is for doom compat. + man->gamepath[i].flags |= GAMEDIR_USEBASEDIR; + man->gamepath[i].flags |= GAMEDIR_READONLY; //must also be readonly, just in case. + man->gamepath[i].path = Z_StrDup(newdir); + break; + } + } + if (!strncmp(newdir, "steam:", 6)) + { //"steam:Subdir/gamedir" + char *sl = strchr(newdir+6, '/'); + if (!sl) + break; //malformed steam link + man->gamepath[i].flags |= GAMEDIR_STEAMGAME; + *sl = 0; + if (!FS_GamedirIsOkay(sl+1)) + break; + *sl = '/'; man->gamepath[i].path = Z_StrDup(newdir); break; } + + if (!FS_GamedirIsOkay(newdir)) + break; + man->gamepath[i].path = Z_StrDup(newdir); + break; } - if (i == sizeof(man->gamepath) / sizeof(man->gamepath[0])) - { - Con_Printf("Too many game paths specified in manifest\n"); - } + } + if (i == sizeof(man->gamepath) / sizeof(man->gamepath[0])) + { + Con_Printf("Too many game paths specified in manifest\n"); } } //FIXME: these should generate package-manager entries. @@ -731,11 +808,17 @@ static void COM_PathLine(searchpath_t *s) (s->flags & SPF_WRITABLE)?"^[(w)\\tip\\Writable\\desc\\We can probably write here^]":"", (s->handle->GeneratePureCRC)?va("^[(h)\\tip\\Hash: %x^]", s->handle->GeneratePureCRC(s->handle, 0, 0)):""); } +qboolean FS_GameIsInitialised(void) +{ + if (!com_searchpaths && !com_purepaths) + return false; + return true; +} static void COM_Path_f (void) { searchpath_t *s; - if (!com_searchpaths && !com_purepaths) + if (!FS_GameIsInitialised()) { Con_Printf("File system not initialised\n"); Con_Printf("gamedirfile: \"%s\"\n", gamedirfile); @@ -1818,19 +1901,13 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out case FS_BASEGAMEONLY: // fte/ last = NULL; - wasbase = true; for (i = 0; i < countof(fs_manifest->gamepath); i++) { - if (fs_manifest && fs_manifest->gamepath[i].base && fs_manifest->gamepath[i].path) + if (fs_manifest && (fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME) && fs_manifest->gamepath[i].path) { - if (!strcmp(fs_manifest->gamepath[i].path, "*")) + if (fs_manifest->gamepath[i].flags & GAMEDIR_SPECIAL) continue; - if (fs_manifest->gamepath[i].base && !wasbase) - continue; - wasbase = fs_manifest->gamepath[i].base; last = fs_manifest->gamepath[i].path; - if (*last == '*') - last++; } } if (!last) @@ -1847,11 +1924,12 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out { if (fs_manifest && fs_manifest->gamepath[i].path) { - if (*fs_manifest->gamepath[i].path == '*') + qboolean isbase = fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME; + if (fs_manifest->gamepath[i].flags&(GAMEDIR_PRIVATE|GAMEDIR_SPECIAL)) continue; - if (fs_manifest->gamepath[i].base && !wasbase) + if (isbase && !wasbase) continue; - wasbase = fs_manifest->gamepath[i].base; + wasbase = isbase; last = fs_manifest->gamepath[i].path; } } @@ -1866,11 +1944,10 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out last = NULL; for (i = 0; i < countof(fs_manifest->gamepath); i++) { - if (fs_manifest && fs_manifest->gamepath[i].base && fs_manifest->gamepath[i].path) + if (fs_manifest && (fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME) && fs_manifest->gamepath[i].path) { - if (*fs_manifest->gamepath[i].path == '*') + if (fs_manifest->gamepath[i].flags&(GAMEDIR_PRIVATE|GAMEDIR_SPECIAL)) continue; - wasbase = fs_manifest->gamepath[i].base; last = fs_manifest->gamepath[i].path; } } @@ -2536,7 +2613,7 @@ static int QDECL FS_SortWildDataFiles(const void *va, const void *vb) const char *na=qsortsucks+a->nameofs, *nb=qsortsucks+b->nameofs; //sort by modification time... - if (a->mtime != b->mtime && a->mtime && b->mtime) + if (a->mtime != b->mtime && a->mtime && b->mtime && !dpcompat_ignoremodificationtimes.ival) return a->mtime > b->mtime; //then fall back and sort by name @@ -3279,7 +3356,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths) //#define RMQCFG "sv_bigcoords 1\n" #ifdef HAVE_SSL -#define UPDATEURL(g) "https://updates.triptohell.info/downloadables.php?game=" #g +#define UPDATEURL(g) "/downloadables.php?game=" #g #else #define UPDATEURL(g) NULL #endif @@ -3879,6 +3956,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) searchpath_t *next; int i, j; int orderkey; + unsigned int fl; COM_AssertMainThread("FS_ReloadPackFilesFlags"); COM_WorkerFullSync(); @@ -3915,23 +3993,9 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) for (i = 0; i < countof(fs_manifest->gamepath); i++) { char *dir = fs_manifest->gamepath[i].path; - if (dir && fs_manifest->gamepath[i].base) + if (dir && (fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME)) { - //don't allow leading dots, hidden files are evil. - //don't allow complex paths. those are evil too. - if (!*dir || *dir == '.' || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/") - || strstr(dir, "\\") || strstr(dir, ":") ) - { - Con_Printf ("Gamedir should be a single filename, not \"%s\"\n", dir); - continue; - } - - //some gamedirs should never be used... - if (!Q_strncasecmp(dir, "downloads", 9) || !Q_strncasecmp(dir, "docs", 4) || !Q_strncasecmp(dir, "help", 4)) - { - Con_Printf ("Gamedir should not be \"%s\"\n", dir); - continue; - } + //paths should be validated before here, when parsing the manifest. #ifdef NQPROT //vanilla NQ uses a slightly different protocol when started with -rogue or -hipnotic (and by extension -quoth). @@ -3940,9 +4004,14 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) standard_quake = false; #endif - //paths equal to '*' actually result in loading packages without an actual gamedir. note that this does not imply that we can write anything. - if (!strcmp(dir, "*")) - { + fl = SPF_EXPLICIT; + if (!(fs_manifest->gamepath[i].flags&GAMEDIR_READONLY)) + fl |= SPF_WRITABLE; + if (fs_manifest->gamepath[i].flags&GAMEDIR_PRIVATE) + fl |= SPF_PRIVATE; + + if (fs_manifest->gamepath[i].flags&GAMEDIR_USEBASEDIR) + { //for doom - loading packages without an actual gamedir. note that this does not imply that we can write anything. searchpathfuncs_t *handle = VFSOS_OpenPath(NULL, NULL, com_gamepath, com_gamepath, ""); searchpath_t *search = (searchpath_t*)Z_Malloc (sizeof(searchpath_t)); search->flags = 0; @@ -3950,19 +4019,26 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) Q_strncpyz(search->purepath, "", sizeof(search->purepath)); Q_strncpyz(search->logicalpath, com_gamepath, sizeof(search->logicalpath)); - FS_AddDataFiles(&oldpaths, search->purepath, search->logicalpath, search, SPF_EXPLICIT, reloadflags); + FS_AddDataFiles(&oldpaths, search->purepath, search->logicalpath, search, fl, reloadflags); handle->ClosePath(handle); Z_Free(search); } - else if (*dir == '*') + else if (fs_manifest->gamepath[i].flags&GAMEDIR_STEAMGAME) { - //paths with a leading * are private, and not announced to clients that ask what the current gamedir is. - FS_AddGameDirectory(&oldpaths, dir+1, reloadflags, SPF_EXPLICIT|SPF_PRIVATE|SPF_WRITABLE); + char steamdir[MAX_OSPATH]; + char *sl; + dir += 6; + sl = strchr(dir, '/'); + if (*sl) + { + if (Sys_SteamHasFile(steamdir, sizeof(steamdir), dir, "")) + FS_AddSingleGameDirectory(&oldpaths, /*pure*/dir, steamdir, reloadflags, SPF_COPYPROTECTED|(fl&~SPF_WRITABLE)); + } } else { - FS_AddGameDirectory(&oldpaths, dir, reloadflags, SPF_EXPLICIT|SPF_WRITABLE); + FS_AddGameDirectory(&oldpaths, dir, reloadflags, fl); } } } @@ -3976,7 +4052,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) for (i = 0; i < countof(fs_manifest->gamepath); i++) { char *dir = fs_manifest->gamepath[i].path; - if (dir && !fs_manifest->gamepath[i].base) + if (dir && !(fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME)) { //don't allow leading dots, hidden files are evil. //don't allow complex paths. those are evil too. @@ -3990,7 +4066,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) for (j = 0; j < countof(fs_manifest->gamepath); j++) { char *dir2 = fs_manifest->gamepath[j].path; - if (dir2 && fs_manifest->gamepath[j].base && !strcmp(dir, dir2)) + if (dir2 && (fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME) && !strcmp(dir, dir2)) break; } if (j < countof(fs_manifest->gamepath)) @@ -4521,25 +4597,27 @@ qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *base #if defined(__linux__) || defined(__unix__) || defined(__apple__) #include -static qboolean Sys_SteamHasFile(char *basepath, int basepathlen, char *steamdir, char *fname) +static qboolean Sys_SteamHasFile(char *basepath, int basepathlen, char *steamdir, char *fname) //returns the base system path { /* Find where Valve's Steam distribution platform is installed. Then take a look at that location for the relevent installed app. */ + //FIXME: we ought to parse steamapps/libraryfolders.vdf (json) and read the "1" etc for games installed on different drives. default drive only for now. FILE *f; - char *ev = getenv("HOME"); - if (ev && *ev) + char *userhome = getenv("HOME"); + if (userhome && *userhome) { - Q_snprintfz(basepath, basepathlen, "%s/.steam/steam/SteamApps/common/%s", ev, steamdir); + Q_snprintfz(basepath, basepathlen, "%s/.steam/steam/steamapps/common/%s", userhome, steamdir); if ((f = fopen(va("%s/%s", basepath, fname), "rb"))) { fclose(f); return true; } - Q_snprintfz(basepath, basepathlen, "%s/.local/share/Steam/SteamApps/common/%s", ev, steamdir); + //steam apparently used to be more standard, or something? FIXME: yet still not xdg? no idea. + Q_snprintfz(basepath, basepathlen, "%s/.local/share/Steam/SteamApps/common/%s", userhome, steamdir); if ((f = fopen(va("%s/%s", basepath, fname), "rb"))) { fclose(f); @@ -4548,6 +4626,11 @@ static qboolean Sys_SteamHasFile(char *basepath, int basepathlen, char *steamdir } return false; } +#else +static qboolean Sys_SteamHasFile(char *basepath, int basepathlen, char *steamdir, char *fname) //returns the base system path +{ + return false; +} #endif qboolean Sys_DoDirectoryPrompt(char *basepath, size_t basepathsize, const char *poshname, const char *savedname) @@ -5708,7 +5791,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean { #ifdef _WIN32 if (!fixedbasedir) - Sys_Error("No recognised game data found in working directory.\n"); + Sys_Error("No recognised game data found in working directory:\n%s", com_gamepath); #endif man = FS_Manifest_Parse(NULL, "FTEManifestVer 1\n" @@ -5763,7 +5846,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean //if there's no base dirs, edit the manifest to give it its default ones. for (j = 0; j < sizeof(man->gamepath) / sizeof(man->gamepath[0]); j++) { - if (man->gamepath[j].path && man->gamepath[j].base) + if (man->gamepath[j].path && (man->gamepath[j].flags&GAMEDIR_BASEGAME)) break; } if (j == sizeof(man->gamepath) / sizeof(man->gamepath[0])) @@ -5778,7 +5861,25 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean if (!man->downloadsurl && gamemode_info[i].downloadsurl) { - Cmd_TokenizeString(va("downloadsurl \"%s\"", gamemode_info[i].downloadsurl), false, false); +#ifndef FTE_TARGET_WEB + if (*gamemode_info[i].downloadsurl == '/') + { + conchar_t musite[256], *e; + char site[256]; + char *oldprefix = "http://fte."; + char *newprefix = "https://updates."; + e = COM_ParseFunString(CON_WHITEMASK, ENGINEWEBSITE, musite, sizeof(musite), false); + COM_DeFunString(musite, e, site, sizeof(site), true, true); + if (!strncmp(site, oldprefix, strlen(oldprefix))) + { + memmove(site+strlen(newprefix), site+strlen(oldprefix), strlen(site)-strlen(oldprefix)); + memcpy(site, newprefix, strlen(newprefix)); + } + Cmd_TokenizeString(va("downloadsurl \"%s%s\"", site, gamemode_info[i].downloadsurl), false, false); + } + else +#endif + Cmd_TokenizeString(va("downloadsurl \"%s\"", gamemode_info[i].downloadsurl), false, false); FS_Manifest_ParseTokens(man); } if (!man->protocolname) @@ -5838,10 +5939,6 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean { qboolean oldhome = com_homepathenabled; COM_InitHomedir(man); - com_homepathenabled = com_homepathusable; - - if (man->disablehomedir && !COM_CheckParm("-usehome")) - com_homepathenabled = false; if (com_homepathenabled != oldhome) { @@ -6394,19 +6491,16 @@ void FS_ArbitraryFile_c(int argn, const char *partial, struct xcommandargcomplet } } -#if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) && !defined(_XBOX) -#elif !defined(NOSTDIO) -#include -#endif -static void COM_InitHomedir(ftemanifest_t *man) -{ - int i; - qboolean usehome; - - //FIXME: this should come from the manifest, as fte_GAME or something +//FIXME: this should come from the manifest, as fte_GAME or something #ifdef _WIN32 - #define HOMESUBDIR FULLENGINENAME + //windows gets formal names... + #ifdef GAME_FULLNAME + #define HOMESUBDIR GAME_FULLNAME + #else + #define HOMESUBDIR FULLENGINENAME + #endif #else + //unix gets short names... #ifdef GAME_SHORTNAME #define HOMESUBDIR GAME_SHORTNAME #else @@ -6414,169 +6508,253 @@ static void COM_InitHomedir(ftemanifest_t *man) #endif #endif - usehome = false; +#if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) && !defined(_XBOX) +//so this is kinda screwy +//"CSIDL_LOCAL_APPDATA/FTE Quake" is what we switched to, but we only use it if the other home dirs don't exist +//"CSIDL_PERSONAL(My Documents)/My Games/FTE Quake" is what we used to use... but personal now somehow means upload-to-internet in a nicely insecure we-own-all-your-data kind of way... +//"%USERPROFILE%/My Documents/My Games/FTE Quake" is an attempt to fall back to the earlier lame path if everything else fails +//"%USERPROFILE%/Saved Games/FTE Quake" is what we probably should be using. I don't know who comes up with these random paths. We have updates+downloads+etc as well as just saves, so we prioritise localdata instead (stuff that you do NOT want microsoft to upload to the internet all the time). +#include +static qboolean FS_GetBestHomeDir(ftemanifest_t *manifest) +{ //win32 sucks. + qboolean usehome = false; + HRESULT (WINAPI *dSHGetFolderPathW) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, wchar_t *pszPath) = NULL; + HRESULT (WINAPI *dSHGetKnownFolderPath) (const GUID *const rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath) = NULL; + dllfunction_t funcs[] = + { + {(void**)&dSHGetFolderPathW, "SHGetFolderPathW"}, + {NULL,NULL} + }; + dllfunction_t funcsvista[] = + { + {(void**)&dSHGetKnownFolderPath, "SHGetKnownFolderPath"}, + {NULL,NULL} + }; + DWORD winver = (DWORD)LOBYTE(LOWORD(GetVersion())); + + enum + { + WINHOME_LOCALDATA, + WINHOME_SAVEDGAMES, + WINHOME_DOCUMENTS, + WINHOME_COUNT + }; + + struct + { + char path[MAX_OSPATH]; + } homedir[WINHOME_COUNT]; + int i; + + /*HMODULE shfolder =*/ Sys_LoadLibrary("shfolder.dll", funcs); + /*HMODULE shfolder =*/ Sys_LoadLibrary("shell32.dll", funcsvista); + + if (dSHGetKnownFolderPath) + { + wchar_t *wide; + static const GUID qFOLDERID_SavedGames = {0x4c5c32ff, 0xbb9d, 0x43b0, {0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4}}; + #define qKF_FLAG_CREATE 0x00008000 + if (SUCCEEDED(dSHGetKnownFolderPath(&qFOLDERID_SavedGames, qKF_FLAG_CREATE, NULL, &wide))) + { + narrowen(homedir[WINHOME_SAVEDGAMES].path, sizeof(homedir[WINHOME_SAVEDGAMES].path), wide); + CoTaskMemFree(wide); + } + } + + if (dSHGetFolderPathW) + { + wchar_t wfolder[MAX_PATH]; + if (dSHGetFolderPathW(NULL, 0x5/*CSIDL_PERSONAL*/, NULL, 0, wfolder) == S_OK) + { + narrowen(homedir[WINHOME_DOCUMENTS].path, sizeof(homedir[WINHOME_DOCUMENTS].path), wfolder); + Q_strncatz(homedir[WINHOME_DOCUMENTS].path, "/My Games", sizeof(homedir[WINHOME_DOCUMENTS].path)); + } + //at some point, microsoft (in their infinitesimal wisdom) decided that 'CSIDL_PERSONAL' should mean 'CSIDL_GIVEITALLTOMICROSOFT' + //so use the old/CSIDL_NOTACTUALLYPERSONAL path by default for compat, but if there's nothing there then switch to CSIDL_LOCAL_APPDATA instead + if (dSHGetFolderPathW(NULL, 0x1c/*CSIDL_LOCAL_APPDATA*/, NULL, 0, wfolder) == S_OK) + { + narrowen(homedir[WINHOME_LOCALDATA].path, sizeof(homedir[WINHOME_LOCALDATA].path), wfolder); + } + } + if (!*homedir[WINHOME_DOCUMENTS].path) + { //guess. sucks for non-english people. + char *ev = getenv("USERPROFILE"); + if (ev) + Q_snprintfz(homedir[WINHOME_DOCUMENTS].path, sizeof(homedir[WINHOME_DOCUMENTS].path), "%s/My Documents/My Games/%s/", ev, HOMESUBDIR); + } +// if (shfolder) +// FreeLibrary(shfolder); + + + for(i = 0; i < countof(homedir); i++) + { + char formal[MAX_OSPATH]; + char informal[MAX_OSPATH]; + if (!*homedir[i].path) + continue; //erk, don't know, not valid/known on this system. + if (!manifest || !manifest->formalname || (/*legacy compat hack case*/ !strcmp(manifest->formalname, "Quake") && strstr(HOMESUBDIR, "Quake"))) + { + Q_snprintfz(formal, sizeof(formal), "%s/%s/", homedir[i].path, HOMESUBDIR); //'FTE Quake' or something hardcoded. + *informal = 0; + } + else + { + if ( strchr(manifest->formalname, '(') || //ugly + strchr(manifest->formalname, '[') || //ugly + strchr(manifest->formalname, '.') || //ugly + strchr(manifest->formalname, '\"') || //ugly (and invalid) + strchr(manifest->formalname, '|') || //invalid + strchr(manifest->formalname, '<') || //invalid + strchr(manifest->formalname, '>') || //invalid + strchr(manifest->formalname, '\\') || //long paths + strchr(manifest->formalname, '/') || //long paths + strchr(manifest->formalname, ':') || //alternative data stream separator + strchr(manifest->formalname, '*') || //wildcard + strchr(manifest->formalname, '?')) //wildcard + *formal = 0; //don't use filenames with awkward chars... + else + Q_snprintfz(formal, sizeof(formal), "%s/%s/", homedir[i].path, manifest->formalname); //'Quake' / 'The Wastes' / etc + Q_snprintfz(informal, sizeof(informal), "%s/%s/", homedir[i].path, manifest->installation); //'quake' / 'wastes' / etc + } + if (*formal && GetFileAttributesU(formal)!=INVALID_FILE_ATTRIBUTES) //path exists, use it. + { + Q_strncpyz(com_homepath, formal, sizeof(com_homepath)); + break; + } + else if (*informal && GetFileAttributesU(informal)!=INVALID_FILE_ATTRIBUTES) //path exists, use it. + { + Q_strncpyz(com_homepath, informal, sizeof(com_homepath)); + break; + } + else if (!*com_homepath) + { + if (!*informal) + Q_strncpyz(com_homepath, formal, sizeof(com_homepath)); + else + Q_strncpyz(com_homepath, informal, sizeof(com_homepath)); + continue; //keep looking, we might still find one that actually exists. + } + } + +#ifdef NPFTE + if (!*com_homepath) + Q_snprintfz(com_homepath, sizeof(com_homepath), "/%s/", HOMESUBDIR); + //as a browser plugin, always use their home directory + return true; +#else + /*would it not be better to just check to see if we have write permission to the basedir?*/ + if (winver >= 0x6) // Windows Vista and above + usehome = true; // always use home directory by default, as Vista+ mimics this behavior anyway + else if (winver >= 0x5) // Windows 2000/XP/2003 + usehome = true; //might as well follow this logic. We use .manifest stuff to avoid getting redirected to obscure locations, so access rights is all that is relevant, not whether we're an admin or not. +#endif + + if (usehome && manifest) + { + DWORD homeattr = GetFileAttributesU(com_homepath); + DWORD baseattr = GetFileAttributesU(com_gamepath); + if (homeattr != INVALID_FILE_ATTRIBUTES && (homeattr & FILE_ATTRIBUTE_DIRECTORY)) + return true; //okay something else already created it. continue using it. + if (baseattr != INVALID_FILE_ATTRIBUTES && (baseattr & FILE_ATTRIBUTE_DIRECTORY)) + { //windows has an _access function, but it doesn't actually bother to check if you're allowed to access it, so its utterly pointless. + //instead try to append nothing to some file that'll probably exist anyway. + //this MAY fail if another program has it open. windows sucks. + vfsfile_t *writetest = VFSOS_Open("conhistory.txt", "a"); + if (!writetest) + return true; //basedir isn't writable, we'll need our home! use it by default. + VFS_CLOSE(writetest); + } + //else don't use it (unless -usehome, anyway) + } + + return false; +} +#elif defined(NOSTDIO) +static qboolean FS_GetBestHomeDir(void) +{ //no studio? webgl port? no file system access = no homedirs! + return false; +} +#else +#include +static qboolean FS_GetBestHomeDir(ftemanifest_t *man) +{ + qboolean usehome = false; + + //on unix, we use environment settings. + //if $HOME/.fte/ exists then we use that because of legacy reasons. + //but if it doesn't exist then we use $XDG_DATA_HOME/.fte instead + //we used to use $HOME/.#HOMESUBDIR/ but this is now only used if it actually exists AND the new path doesn't. + //new installs use $XDG_DATA_HOME/#HOMESUBDIR/ instead + char *ev = getenv("FTEHOME"); + if (ev && *ev) + { + if (ev[strlen(ev)-1] == '/') + Q_strncpyz(com_homepath, ev, sizeof(com_homepath)); + else + Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/", ev); + usehome = true; // always use home on unix unless told not to + ev = NULL; + } + else + ev = getenv("HOME"); + if (ev && *ev) + { + const char *xdghome; + char oldhome[MAX_OSPATH]; + char newhome[MAX_OSPATH]; + struct stat s; + char *installation = (man && man->installation && *man->installation)?man->installation:HOMESUBDIR; + usehome = (man && man->installation && *man->installation)?true:false; //use it if we're running a game, otherwise don't bother if we're still loading. + + xdghome = getenv("XDG_DATA_HOME"); + if (!xdghome || !*xdghome) + xdghome = va("%s/.local/share", ev); + if (xdghome[strlen(xdghome)-1] == '/') + Q_snprintfz(newhome, sizeof(newhome), "%s%s/", xdghome, installation); + else + Q_snprintfz(newhome, sizeof(newhome), "%s/%s/", xdghome, installation); + + if (ev[strlen(ev)-1] == '/') + Q_snprintfz(oldhome, sizeof(oldhome), "%s.%s/", ev, installation); + else + Q_snprintfz(oldhome, sizeof(oldhome), "%s/.%s/", ev, installation); + + if (stat(newhome, &s) == -1 && stat(oldhome, &s) != -1) + Q_strncpyz(com_homepath, oldhome, sizeof(com_homepath)); + else + Q_strncpyz(com_homepath, newhome, sizeof(com_homepath)); + } + + if (usehome && man) + { + struct stat statbuf; + if (stat(com_homepath, &statbuf) >= 0 && (statbuf.st_mode & S_IFMT)==S_IFDIR) + return true; //okay something else already created it. continue using it. + if (access(com_gamepath, W_OK) < 0) + return true; //baesdir isn't writable, we'll need our home! use it by default. + //else don't use it (unless -usehome, anyway) + } + return false; +} +#endif +static void COM_InitHomedir(ftemanifest_t *man) +{ + int i; //assume the home directory is the working directory. *com_homepath = '\0'; -#if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) && !defined(_XBOX) - { //win32 sucks. - HRESULT (WINAPI *dSHGetFolderPathW) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, wchar_t *pszPath) = NULL; - dllfunction_t funcs[] = - { - {(void**)&dSHGetFolderPathW, "SHGetFolderPathW"}, - {NULL,NULL} - }; - DWORD winver = (DWORD)LOBYTE(LOWORD(GetVersion())); - /*HMODULE shfolder =*/ Sys_LoadLibrary("shfolder.dll", funcs); - - if (dSHGetFolderPathW) - { - wchar_t wfolder[MAX_PATH]; - char folder[MAX_OSPATH]; - if (dSHGetFolderPathW(NULL, 0x5/*CSIDL_PERSONAL*/, NULL, 0, wfolder) == S_OK) - { - narrowen(folder, sizeof(folder), wfolder); - Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/My Games/%s/", folder, HOMESUBDIR); - } - //at some point, microsoft (in their infinitesimal wisdom) decided that 'CSIDL_PERSONAL' should mean 'CSIDL_GIVEITALLTOMICROSOFT' - //so use the old/CSIDL_NOTACTUALLYPERSONAL path by default for compat, but if there's nothing there then switch to CSIDL_LOCAL_APPDATA instead - if ((!*com_homepath||!GetFileAttributesU(com_homepath)) && dSHGetFolderPathW(NULL, 0x1c/*CSIDL_LOCAL_APPDATA*/, NULL, 0, wfolder) == S_OK) - { - narrowen(folder, sizeof(folder), wfolder); - Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/%s/", folder, HOMESUBDIR); - } - } -// if (shfolder) -// FreeLibrary(shfolder); - - if (!*com_homepath) - { - char *ev = getenv("USERPROFILE"); - if (ev) - Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/My Documents/My Games/%s/", ev, HOMESUBDIR); - } - -#ifdef NPFTE - if (!*com_homepath) - Q_snprintfz(com_homepath, sizeof(com_homepath), "/%s/", HOMESUBDIR); - //as a browser plugin, always use their home directory - usehome = true; -#else - /*would it not be better to just check to see if we have write permission to the basedir?*/ - if (winver >= 0x6) // Windows Vista and above - usehome = true; // always use home directory by default, as Vista+ mimics this behavior anyway - else if (winver >= 0x5) // Windows 2000/XP/2003 - { - HMODULE advapi32; - BOOL (WINAPI *dCheckTokenMembership) (HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember) = NULL; - dllfunction_t funcs[] = - { - {(void**)&dCheckTokenMembership, "CheckTokenMembership"}, - {NULL,NULL} - }; - advapi32 = Sys_LoadLibrary("advapi32.dll", funcs); - if (advapi32) - { - if (dCheckTokenMembership) - { - // on XP systems, only use a home directory by default if we're a limited user or if we're on a network - BOOL isadmin, isonnetwork; - SID_IDENTIFIER_AUTHORITY ntauth = {SECURITY_NT_AUTHORITY}; - PSID adminSID, networkSID; - - isadmin = AllocateAndInitializeSid(&ntauth, - 2, - SECURITY_BUILTIN_DOMAIN_RID, - DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, - &adminSID); - - // just checking the network rid should be close enough to matching domain logins - isonnetwork = AllocateAndInitializeSid(&ntauth, - 1, - SECURITY_NETWORK_RID, - 0, 0, 0, 0, 0, 0, 0, - &networkSID); - - if (isadmin && !dCheckTokenMembership(0, adminSID, &isadmin)) - isadmin = 0; - - if (isonnetwork && !dCheckTokenMembership(0, networkSID, &isonnetwork)) - isonnetwork = 0; - - usehome = isonnetwork || !isadmin; - - FreeSid(networkSID); - FreeSid(adminSID); - } - - Sys_CloseLibrary(advapi32); - } - } -#endif - } -#elif !defined(NOSTDIO) - { - //on unix, we use environment settings. - //if $HOME/.fte/ exists then we use that because of legacy reasons. - //but if it doesn't exist then we use $XDG_DATA_HOME/.fte instead - //we used to use $HOME/.#HOMESUBDIR/ but this is now only used if it actually exists AND the new path doesn't. - //new installs use $XDG_DATA_HOME/#HOMESUBDIR/ instead - char *ev = getenv("FTEHOME"); - if (ev && *ev) - { - if (ev[strlen(ev)-1] == '/') - Q_strncpyz(com_homepath, ev, sizeof(com_homepath)); - else - Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/", ev); - usehome = true; // always use home on unix unless told not to - ev = NULL; - } - else - ev = getenv("HOME"); - if (ev && *ev) - { - const char *xdghome; - char oldhome[MAX_OSPATH]; - char newhome[MAX_OSPATH]; - struct stat s; - - xdghome = getenv("XDG_DATA_HOME"); - if (!xdghome || !*xdghome) - xdghome = va("%s/.local/share", ev); - if (man && man->installation) - { - if (xdghome[strlen(xdghome)-1] == '/') - Q_snprintfz(com_homepath, sizeof(com_homepath), "%s%s/", xdghome, *man->installation?man->installation:HOMESUBDIR); - else - Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/%s/", xdghome, *man->installation?man->installation:HOMESUBDIR); - } - else - { - if (xdghome[strlen(xdghome)-1] == '/') - Q_snprintfz(newhome, sizeof(newhome), "%s%s/", xdghome, HOMESUBDIR); - else - Q_snprintfz(newhome, sizeof(newhome), "%s/%s/", xdghome, HOMESUBDIR); - - if (ev[strlen(ev)-1] == '/') - Q_snprintfz(oldhome, sizeof(oldhome), "%s.%s/", ev, HOMESUBDIR); - else - Q_snprintfz(oldhome, sizeof(oldhome), "%s/.%s/", ev, HOMESUBDIR); - - if (stat(newhome, &s) == -1 && stat(oldhome, &s) != -1) - Q_strncpyz(com_homepath, oldhome, sizeof(com_homepath)); - else - Q_strncpyz(com_homepath, newhome, sizeof(com_homepath)); - } - usehome = true; // always use home on unix unless told not to - } - } -#endif - - com_homepathusable = usehome; + if (man && (strstr(man->installation, "..") || strchr(man->installation, '/') || strchr(man->installation, '\\'))) + com_homepathusable = false; //don't even try to generate a relative homedir. + else + com_homepathusable = FS_GetBestHomeDir(man); com_homepathenabled = false; + if (man && man->homedirtype == MANIFEST_NOHOMEDIR) + com_homepathusable = false; + else if (man && man->homedirtype == MANIFEST_FORCEHOMEDIR) + com_homepathusable = true; + i = COM_CheckParm("-homedir"); if (i && i+1numsides; i++) { - j = Fragment_ClipPlaneToBrush(verts, countof(verts), planes, sizeof(planes[0]), brush->numsides, planes[i]); - while (j-- > 0) - AddPointToBounds(verts[j], brush->absmins, brush->absmaxs); + //most brushes are axial, which can save some a little loadtime + if (planes[i][0] == 1) + brush->absmaxs[0] = planes[i][3]; + else if (planes[i][1] == 1) + brush->absmaxs[1] = planes[i][3]; + else if (planes[i][2] == 1) + brush->absmaxs[2] = planes[i][3]; + else if (planes[i][0] == -1) + brush->absmins[0] = -planes[i][3]; + else if (planes[i][1] == -1) + brush->absmins[1] = -planes[i][3]; + else if (planes[i][2] == -1) + brush->absmins[2] = -planes[i][3]; + else + { + j = Fragment_ClipPlaneToBrush(verts, countof(verts), planes, sizeof(planes[0]), brush->numsides, planes[i]); + while (j-- > 0) + AddPointToBounds(verts[j], brush->absmins, brush->absmaxs); + } } } diff --git a/engine/common/net_ssl_gnutls.c b/engine/common/net_ssl_gnutls.c index a3499cb5..d4bf52c1 100644 --- a/engine/common/net_ssl_gnutls.c +++ b/engine/common/net_ssl_gnutls.c @@ -455,20 +455,6 @@ static qboolean QDECL SSL_CloseFile(vfsfile_t *vfs) return true; } -static const qbyte updates_triptohell_certdata[962] = "\x30\x82\x03\xbe\x30\x82\x02\xa6\xa0\x03\x02\x01\x02\x02\x09\x00\xf4\x4e\x13\x08\x32\x85\xb5\x19\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x30\x74\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x4b\x31\x10\x30\x0e\x06\x03\x55\x04\x08\x0c\x07\x45\x6e\x67\x6c\x61\x6e\x64\x31\x0f\x30\x0d\x06\x03\x55\x04\x07\x0c\x06\x4c\x6f\x6e\x64\x6f\x6e\x31\x0e\x30\x0c\x06\x03\x55\x04\x0a\x0c\x05\x46\x54\x45\x51\x57\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x55\x70\x64\x61\x74\x65\x73\x31\x20\x30\x1e\x06\x03\x55\x04\x03\x0c\x17\x75\x70\x64\x61\x74\x65\x73\x2e\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x1e\x17\x0d\x31\x39\x30\x35\x33\x31\x31\x30\x30\x39\x31\x39\x5a\x17\x0d\x32\x39\x30\x35\x32\x38\x31\x30\x30\x39\x31\x39\x5a\x30\x74\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x4b\x31\x10\x30\x0e\x06\x03\x55\x04\x08\x0c\x07\x45\x6e\x67\x6c\x61\x6e\x64\x31\x0f\x30\x0d\x06\x03\x55\x04\x07\x0c\x06\x4c\x6f\x6e\x64\x6f\x6e\x31\x0e\x30\x0c\x06\x03\x55\x04\x0a\x0c\x05\x46\x54\x45\x51\x57\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x55\x70\x64\x61\x74\x65\x73\x31\x20\x30\x1e\x06\x03\x55\x04\x03\x0c\x17\x75\x70\x64\x61\x74\x65\x73\x2e\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xaa\xb5\x9c\xcc\xe8\xbd\xad\x1c\x7f\x6b\x1c\xc9\x04\xe6\x2c\x10\xac\x99\xeb\x67\x0b\x9c\x24\xb8\x90\x77\xde\xaa\x44\xa2\x15\x31\x61\x52\x4d\xeb\x8f\x56\xb8\xaf\xc1\x2f\x66\xdd\x55\x8d\xd6\xec\xc3\xa4\x93\x8c\x86\xeb\xaf\x89\x17\x19\x2e\x6c\xc2\xd4\xf9\x92\xac\x2e\x73\x99\x56\xf2\xc3\xc4\x14\x56\x4a\x0d\xbe\x51\xc9\x8f\x4e\x92\x20\x2b\xae\x05\x0c\x7e\x87\xa5\x02\xe1\xc0\x7d\x71\xa7\x38\x72\x47\x3f\x31\x07\x90\xb0\x6d\xcf\xae\xb6\xdb\xeb\x39\xaa\x5f\xb4\x6f\x0c\x63\x2a\x21\x65\x36\xaa\x6b\xac\x97\xb6\xbe\x20\xa4\x87\x36\xbf\x35\xc5\xa6\x31\xe4\x9d\x85\xf3\xae\x8f\x6b\xf8\x59\x75\x0f\xb5\x5d\x31\x40\x39\x2e\xea\x48\x65\xdf\x91\xe3\x06\xfb\xb2\xec\xdc\xd0\x90\x94\xd6\x68\x4d\x62\x25\x9a\x3d\xc3\x74\x17\x7d\x0e\xe2\x1e\x34\xbf\x02\x85\xc4\x40\x88\x91\xeb\xe0\xf5\x92\x56\x42\x4f\xa6\x4c\x17\x88\xb2\x89\xd2\xec\x60\x54\x97\x20\x0a\xca\xf0\xd1\x33\x3f\x5b\x66\xb7\x8a\x42\x72\x67\xc9\x47\x83\xb3\xd4\x1e\xa8\x44\xbf\x5a\x1a\x85\x79\xee\xf8\x80\xde\x19\x1d\xc5\xdd\x50\x42\x10\x17\xb7\xc3\xd4\xf1\xcb\x8a\xb8\x71\x55\x31\x02\x03\x01\x00\x01\xa3\x53\x30\x51\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x2c\x68\x81\x8f\x40\x8c\x40\x42\x9f\xbd\xc5\x0b\x36\xfb\xe2\x76\xeb\x8d\xb4\xf3\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x2c\x68\x81\x8f\x40\x8c\x40\x42\x9f\xbd\xc5\x0b\x36\xfb\xe2\x76\xeb\x8d\xb4\xf3\x30\x0f\x06\x03\x55\x1d\x13\x01\x01\xff\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x0c\x01\x35\x32\xb8\xe7\x96\xba\x3e\x53\x8c\x78\x41\xab\x9b\x7f\xe2\x7a\x80\x5a\xc9\x88\x06\x29\x28\xf9\x50\x7f\xcc\xb6\xcc\x34\x03\x45\x32\x79\x63\xe7\xde\x9c\x46\x29\xf4\xaf\x32\x72\x26\x11\xa0\x7b\x52\x23\x0a\xd5\x51\x91\x79\xf2\x50\x61\x80\x72\x40\xe7\x85\xb0\x13\x1d\x98\xdb\x14\x23\x59\xa4\xbc\xe9\xe0\x1b\xc0\x38\x33\x96\xbc\xbb\x56\x47\xcc\xbd\xe8\x40\x49\xdf\xaa\x64\x7e\x29\xe5\x9d\x40\xa5\x1a\x5c\x45\x1f\x5a\x77\x59\xfe\x7a\xb8\xf8\x4d\xc4\x9b\x31\xe6\x08\xc4\x95\xfa\x91\x8f\x91\x9f\x3c\xc4\x82\xb9\xf1\x6d\xa8\xa6\xc4\x09\xb1\xe9\xa8\x60\x9b\xaa\x4c\x79\xf0\x99\xb8\xad\x63\xb1\xe4\xc0\xaf\xf0\xdf\xc9\x33\x53\x4d\x09\xe4\x3f\x8d\x9e\x38\xc6\x93\xff\xcc\x91\x46\x7e\x67\x28\x61\xaa\xc7\x0b\xe2\xd8\x8c\xe4\xec\x8d\x44\xe7\x6a\x14\x78\x91\x7d\xec\xc7\x07\xed\xc9\x58\xdb\x35\xd4\x70\x06\x06\x39\x8d\x4b\x80\x2c\xb6\xa8\x79\x5c\x94\x15\x6c\x34\x06\x5c\xd7\xc5\x42\xc0\x72\x01\x71\x07\xf5\x25\x6a\xd0\x24\x86\xcd\x1b\x21\x07\xae\x40\xf8\xc1\xe4\x23\x0d\xa0\xc0\x23\xf0\x07\xba\xdc\x34\x5d\x47\xcf\x4b\x7b\xd5\x5d"; -//static const qbyte fte_triptohell_certdata[917] = "\x30\x82\x03\x91\x30\x82\x02\x79\xa0\x03\x02\x01\x02\x02\x09\x00\xb5\x71\x47\x8d\x5e\x66\xf1\xd9\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x30\x5f\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x11\x30\x0f\x06\x03\x55\x04\x07\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x0c\x30\x0a\x06\x03\x55\x04\x0a\x0c\x03\x46\x54\x45\x31\x1c\x30\x1a\x06\x03\x55\x04\x03\x0c\x13\x66\x74\x65\x2e\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x1e\x17\x0d\x31\x34\x31\x32\x32\x35\x30\x30\x35\x38\x31\x34\x5a\x17\x0d\x31\x37\x30\x33\x30\x34\x30\x30\x35\x38\x31\x34\x5a\x30\x5f\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x11\x30\x0f\x06\x03\x55\x04\x07\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x0c\x30\x0a\x06\x03\x55\x04\x0a\x0c\x03\x46\x54\x45\x31\x1c\x30\x1a\x06\x03\x55\x04\x03\x0c\x13\x66\x74\x65\x2e\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xdd\xb8\x7c\x69\x3d\x63\x95\xe3\x88\x15\xfd\xad\x93\x5e\x6b\x97\xfb\x74\xba\x1f\x83\x33\xe5\x8a\x8d\x8f\xb0\xbf\xf9\xd3\xa1\x2c\x65\x53\xa7\xef\xd3\x0f\xdc\x03\x60\x0a\x40\xef\xa8\xef\x3f\xb3\xd9\x8d\x31\x39\x12\x8a\xd8\x0e\x24\x8f\xe5\x58\x26\x86\x4c\x76\x6c\x59\x9a\xab\xea\x1c\x3d\xfb\x62\x62\xad\xaf\xd6\x00\x33\x76\x2d\xbb\xeb\xe8\xec\xb4\x76\x4f\xb0\xbe\xcf\xf0\x46\x94\x40\x02\x99\xd4\xb2\x71\x71\xd6\xf5\x1f\xc3\x4f\x1e\x1e\xb4\x0d\x82\x49\xc4\xa2\xdc\xae\x6f\x4e\x3a\xf9\x0e\xdd\xf4\xd2\x53\xe3\xe7\x7d\x58\x79\xf4\xce\x1f\x6c\xac\x81\x8c\x8c\xe1\x03\x5b\x22\x56\x92\x19\x4f\x74\xc0\x36\x41\xac\x1b\xfa\x9e\xf7\x2a\x0f\xd6\x4b\xcc\x9a\xca\x67\x87\xb7\x95\xdf\xb7\xd4\x7d\x8c\xcc\xa9\x25\xde\xdd\x8c\x1b\xd7\x32\xf2\x84\x25\x46\x7b\x10\x55\xf9\x80\xfd\x5d\xad\xab\xf9\x4c\x1f\xc0\xa5\xd1\x3f\x01\x86\x4d\xfa\x57\xab\x7a\x6d\xec\xf1\xdb\xf4\xad\xf2\x33\xcd\xa0\xed\xfe\x1b\x27\x55\x56\xba\x8c\x47\x70\x16\xd5\x75\x17\x8e\x80\xaa\x49\x5e\x93\x83\x1d\x6f\x1f\x2c\xf7\xa7\x64\xe6\x2e\x88\x8e\xff\x70\x5a\x41\x52\xae\x93\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x4e\x76\x4a\xce\x7b\x45\x14\x39\xeb\x9c\x28\x56\xb5\x7b\x8a\x18\x6f\x22\x17\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x4e\x76\x4a\xce\x7b\x45\x14\x39\xeb\x9c\x28\x56\xb5\x7b\x8a\x18\x6f\x22\x17\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x48\x22\x65\xed\x2e\xc5\xed\xbb\xe9\x40\x6c\x80\xc4\x63\x19\xd1\x00\xb4\x30\x34\x17\x7c\x7c\xbd\x1b\xc5\xa9\x43\x0c\x92\x6e\xd6\x2d\x11\x6c\x0d\xa6\xda\x30\xe9\xf7\x46\x7b\x01\xe4\x53\x23\xae\x88\xd1\xf2\xed\xca\x84\x06\x19\x97\xb9\x06\xfb\xda\xec\x72\x2d\x15\x20\xd2\x8f\x66\xad\xb5\xdd\x4b\x4f\xdf\x7e\xaf\xa3\x6c\x7f\x53\x32\x8f\xe2\x19\x5c\x44\x98\x86\x31\xee\xb4\x03\xe7\x27\xa1\x83\xab\xc3\xce\xb4\x9a\x01\xbe\x8c\x64\x2e\x2b\xe3\x4e\x55\xdf\x95\xeb\x16\x87\xbd\xfa\x11\xa2\x3e\x38\x92\x97\x36\xe9\x65\x60\xf3\xac\x68\x44\xb3\x51\x54\x3a\x42\xa8\x98\x9b\xee\x1b\x9e\x79\x6a\xaf\xc0\xbe\x41\xc4\xb1\x96\x42\xd9\x94\xef\x49\x5b\xbe\x2d\x04\xb9\xfb\x92\xbb\xdc\x0e\x29\xfd\xee\xa9\x68\x09\xf9\x9f\x69\x8b\x3d\xe1\x4b\xee\x24\xf9\xfe\x02\x3a\x0a\xb8\xcd\x6c\x07\x43\xa9\x4a\xe7\x03\x34\x2e\x72\xa7\x81\xaa\x40\xa9\x98\x5d\x97\xee\x2a\x99\xc6\x8f\xe8\x6f\x98\xa2\x85\xc9\x0d\x04\x19\x43\x6a\xd3\xc7\x15\x4c\x4b\xbc\xa5\xb8\x9f\x38\xf3\x43\x83\x0c\xef\x97\x6e\xa6\x20\xde\xc5\xd3\x1e\x3e\x5d\xcd\x58\x3d\x5c\x55\x7a\x90\x94"; -//static const qbyte triptohell_certdata[933] = "\x30\x82\x03\xa1\x30\x82\x02\x89\xa0\x03\x02\x01\x02\x02\x09\x00\xea\xb7\x13\xcf\x55\xe5\xe8\x8c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x30\x67\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x11\x30\x0f\x06\x03\x55\x04\x07\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x18\x30\x16\x06\x03\x55\x04\x0a\x0c\x0f\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x31\x18\x30\x16\x06\x03\x55\x04\x03\x0c\x0f\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x1e\x17\x0d\x31\x34\x31\x32\x32\x35\x30\x30\x35\x38\x33\x37\x5a\x17\x0d\x31\x37\x30\x33\x30\x34\x30\x30\x35\x38\x33\x37\x5a\x30\x67\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x11\x30\x0f\x06\x03\x55\x04\x07\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x18\x30\x16\x06\x03\x55\x04\x0a\x0c\x0f\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x31\x18\x30\x16\x06\x03\x55\x04\x03\x0c\x0f\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xd8\x77\x62\xf6\x74\xa7\x75\xde\xda\x09\xae\x9e\x76\x7a\xc6\x2a\xcf\x9a\xbe\xc6\xb9\x6d\xe2\xca\x0f\x2d\x95\xb8\x89\x93\xf7\x50\x64\x92\x7d\x95\x34\xe4\x6e\xef\x52\x56\xef\x13\x9a\x3a\xae\x84\x5b\x57\x82\x04\x86\x74\xbd\x4e\x38\x32\x56\x00\xd6\x34\x9c\x23\xd6\x81\x8e\x29\x77\x45\x61\x20\xdf\x28\xf8\xe5\x61\x83\xec\xe6\xa0\x1a\x75\xa8\x3b\x53\x6f\xc4\x09\x61\x66\x3a\xf0\x81\xbf\x2c\xf5\x8e\xf1\xe2\x35\xe4\x24\x7f\x16\xcc\xce\x60\xa2\x42\x6e\xc2\x3a\x29\x75\x6c\x79\xb0\x99\x9c\xe2\xfe\x27\x32\xb6\xf7\x0d\x71\xfd\x62\x9d\x54\x7c\x40\xb2\xf5\xa0\xa4\x25\x31\x8d\x65\xfd\x3f\x3b\x9b\x7e\x84\x74\x17\x3c\x1f\xec\x50\xcf\x75\xb8\x5c\xca\xfc\x0f\xe8\x47\xd8\x64\xec\x5f\x6c\x45\x9a\x55\x49\x97\x3f\xcb\x49\x34\x71\x0a\x12\x13\xbc\x3d\x53\x81\x17\x9a\x92\x44\x91\x07\xc2\xef\x6d\x64\x86\x5d\xfd\x67\xd5\x99\x38\x95\x46\x74\x6d\xb6\xbf\x29\xc9\x5b\xac\xb1\x46\xd6\x9e\x57\x5c\x7b\x24\x91\xf4\x7c\xe4\x01\x31\x8c\xec\x79\x94\xb7\x3f\xd2\x93\x6d\xe2\x69\xbe\x61\x44\x2e\x8f\x1a\xdc\xa8\x97\xf5\x81\x8e\x0c\xe1\x00\xf2\x71\x51\xf3\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x18\xb2\x6b\x63\xcc\x17\x54\xf6\xf0\xb6\x9e\x62\xa4\x35\xcf\x47\x74\x13\x29\xbf\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x18\xb2\x6b\x63\xcc\x17\x54\xf6\xf0\xb6\x9e\x62\xa4\x35\xcf\x47\x74\x13\x29\xbf\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x7f\x24\x18\x8a\x79\xee\xf9\xeb\xed\x29\x1e\x21\x15\x8a\x53\xc9\xb7\xec\x30\xc4\x85\x9f\x45\x85\x26\x36\xb7\x07\xf3\xf1\xff\x3b\x89\x05\x0a\xd4\x30\x68\x31\x68\x33\xdd\xf6\x58\xa3\x85\x9f\x49\x50\x76\x9a\xc5\x79\x13\xe1\x4d\x67\x0c\xf3\x92\xf0\x1d\x02\x1f\xc4\x5c\xd4\xa1\x0c\x57\xdf\x46\x84\x43\x9f\xb0\xe2\x91\x62\xa8\xe0\x86\x0d\x47\xe1\xd9\x60\x01\xc4\xe0\xda\x6f\x06\x0a\xad\x38\xf3\x66\x68\xc5\xe2\x66\x3e\x47\x83\x65\x64\xcd\xff\xf3\xbb\xa7\xfa\x23\xf1\x82\x5e\x06\x6a\x91\x37\x51\xcd\xb9\x95\x20\x89\xff\xa1\x54\xb2\x76\xcf\x8e\xe1\xcd\x13\x93\x13\xd1\xda\x0d\x0d\xbc\x0f\xd5\x11\x26\xd6\xaf\x60\x0f\x4d\x8a\x4f\x28\xee\x6c\xf1\x99\xdc\xed\x16\xdc\x87\x26\xfd\x23\x8a\xb8\xb0\x20\x0e\xe2\x32\xf5\x8e\xb0\x65\x98\x13\xb8\x4b\x39\x7c\x8c\x98\xa2\x29\x75\x48\x3a\x89\xf9\x61\x77\x6c\x2d\x84\x41\x40\x17\xa6\x50\xc5\x09\x63\x10\xe7\x09\xd4\x5c\xdd\x0e\x71\x16\xaf\xb1\x32\xe4\xc0\xe6\xea\xfd\x26\x55\x07\x40\x95\x84\x48\x62\x04\x10\x92\xb2\xd9\x27\xfb\x8a\xf3\x7c\xe6\xfe\xd4\xfc\xa6\x33\x79\x01\x5c\xc3\x1f\x80\xa8\xf3"; -static struct -{ - char *hostname; - unsigned int datasize; - const qbyte *data; -} knowncerts[] = { -// {"triptohell.info", sizeof(triptohell_certdata), triptohell_certdata}, -// {"fte.triptohell.info", sizeof(fte_triptohell_certdata), fte_triptohell_certdata}, - {"updates.triptohell.info", sizeof(updates_triptohell_certdata), updates_triptohell_certdata}, -}; - static qboolean SSL_CheckUserTrust(gnutls_session_t session, gnutlsfile_t *file, int *errorcode) { #ifdef HAVE_CLIENT @@ -506,50 +492,58 @@ static int QDECL SSL_CheckCert(gnutls_session_t session) qboolean preverified = false; int errcode = GNUTLS_E_CERTIFICATE_ERROR; - size_t i; - for (i = 0; i < countof(knowncerts); i++) + size_t knownsize; + qbyte *knowndata = TLS_GetKnownCertificate(file->certname, &knownsize); + + if (knowndata) { - if (!strcmp(knowncerts[i].hostname, file->certname)) + unsigned int certcount, j; + const gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(session, &certcount); + if (!certlist || !certcount) { - unsigned int certcount, j; - const gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(session, &certcount); - if (certlist && certcount) - { - size_t offset = 0; - - for (j = 0; j < certcount; offset += certlist[j++].size) - { - if (certlist[j].size+offset > knowncerts[i].datasize) - break; //overflow... - if (memcmp(certlist[j].data, knowncerts[i].data+offset, certlist[j].size)) - break; - } - - if (j && j == certcount && offset == knowncerts[i].datasize) - preverified = true; - else - { -#ifdef _DEBUG - for (j = 0, offset = 0; j < certcount; j++) - offset += certlist[j].size; - Con_Printf("%s cert %zu bytes (chain %u)\n", file->certname, offset, certcount); - Con_Printf("\""); - - for (j = 0; j < certcount; j++) - { - unsigned char *data = certlist[j].data; - unsigned int datasize = certlist[j].size, k; - for (k = 0; k < datasize; k++) - Con_Printf("\\x%02x", data[k]); - } - Con_Printf("\"\n\n"); -#endif - Con_Printf(CON_ERROR "%s: Reported certificate does not match known certificate. Possible MITM attack, alternatively just an outdated client.\n", file->certname); - return GNUTLS_E_CERTIFICATE_ERROR; - } - } - break; + BZ_Free(knowndata); + return GNUTLS_E_CERTIFICATE_ERROR; } + else + { + size_t offset = 0; + + for (j = 0; j < certcount; offset += certlist[j++].size) + { + if (certlist[j].size+offset > knownsize) + break; //overflow... + if (memcmp(certlist[j].data, knowndata+offset, certlist[j].size)) + break; + } + + if (j && j == certcount && offset == knownsize) + preverified = true; + else + { +#ifdef _DEBUG + for (j = 0, offset = 0; j < certcount; j++) + offset += certlist[j].size; + Con_Printf("%s cert %zu bytes (chain %u)\n", file->certname, offset, certcount); + Con_Printf("/*%s*/\"", file->certname); + for (j = 0; file->certname[j]; j++) + Con_Printf("\\x%02x", file->certname[j]^0xff); + Con_Printf("\\xff"); + Con_Printf("\\x%02x\\x%02x", (unsigned)offset&0xff, ((unsigned)offset>>8)&0xff); + for (j = 0; j < certcount; j++) + { + unsigned char *data = certlist[j].data; + unsigned int datasize = certlist[j].size, k; + for (k = 0; k < datasize; k++) + Con_Printf("\\x%02x", data[k]^0xff); + } + Con_Printf("\",\n\n"); +#endif + Con_Printf(CON_ERROR "%s: Reported certificate does not match known certificate. Possible MITM attack, alternatively just an outdated client.\n", file->certname); + BZ_Free(knowndata); + return GNUTLS_E_CERTIFICATE_ERROR; + } + } + BZ_Free(knowndata); } #ifdef GNUTLS_HAVE_VERIFY3 diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index eab9f2da..6ac9ef4e 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -8,6 +8,7 @@ I've given up for now. */ #include "winquake.h" +#include "netinc.h" #define SECURITY_WIN32 #include #include @@ -384,32 +385,12 @@ static void SSPI_Encode(sslfile_t *f) SSPI_TryFlushCryptOut(f); } -//these are known sites that use self-signed certificates, or are special enough that we don't trust corporate networks to hack in their own certificate authority for a proxy/mitm -//old static const qbyte triptohell_certdata[933] = "\x30\x82\x03\xa1\x30\x82\x02\x89\xa0\x03\x02\x01\x02\x02\x09\x00\x8b\xd0\x05\x63\x62\xd1\x6a\xe3\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x67\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x42\x44\x31\x0c\x30\x0a\x06\x03\x55\x04\x08\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x07\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x0a\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x0b\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x03\x0c\x03\x42\x61\x64\x31\x12\x30\x10\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x03\x42\x61\x64\x30\x1e\x17\x0d\x31\x34\x31\x32\x32\x34\x32\x32\x34\x32\x34\x37\x5a\x17\x0d\x32\x34\x31\x32\x32\x31\x32\x32\x34\x32\x34\x37\x5a\x30\x67\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x42\x44\x31\x0c\x30\x0a\x06\x03\x55\x04\x08\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x07\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x0a\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x0b\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x03\x0c\x03\x42\x61\x64\x31\x12\x30\x10\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x03\x42\x61\x64\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xaf\x10\x33\xfa\x39\xf5\xae\x2c\x91\x0e\x20\xe6\x3c\x5c\x7c\x1e\xeb\x16\x50\x2f\x05\x30\xfe\x67\xee\xa9\x00\x54\xd9\x4a\x86\xe6\xba\x80\xfb\x1a\x80\x08\x7e\x7b\x13\xe5\x1a\x18\xc9\xd4\x70\xbd\x5d\xc4\x38\xef\x64\xf1\x90\x2c\x53\x49\x93\x24\x36\x3e\x11\x59\x69\xa6\xdf\x37\xb2\x54\x82\x28\x3e\xdd\x30\x75\xa0\x18\xd8\xe1\xf5\x52\x73\x12\x5b\x37\x68\x1c\x59\xbd\x8c\x73\x66\x47\xbc\xcb\x9c\xfe\x38\x92\x8f\x74\xe9\xd1\x2f\x96\xd2\x5d\x6d\x11\x59\xb2\xdc\xbd\x8c\x37\x5b\x22\x76\x98\xe7\xbe\x08\xef\x1e\x99\xc4\xa9\x77\x2c\x9c\x0e\x08\x3c\x8e\xab\x97\x0c\x6a\xd7\x03\xab\xfd\x4a\x1e\x95\xb2\xc2\x9c\x3a\x16\x65\xd7\xaf\x45\x5f\x6e\xe7\xce\x51\xba\xa0\x60\x43\x0e\x07\xc5\x0b\x0a\x82\x05\x26\xc4\x92\x0a\x27\x5b\xfc\x57\x6c\xdf\xe2\x54\x8a\xef\x38\xf1\xf8\xc4\xf8\x51\x16\x27\x1f\x78\x89\x7c\x5b\xd7\x53\xcd\x9b\x54\x2a\xe6\x71\xee\xe4\x56\x2e\xa4\x09\x1a\x61\xf7\x0f\x97\x22\x94\xd7\xef\x21\x6c\xe6\x81\xfb\x54\x5f\x09\x92\xac\xd2\x7c\xab\xd5\xa9\x81\xf4\xc9\xb7\xd6\xbf\x68\xf8\x4f\xdc\xf3\x60\xa3\x3b\x29\x92\x9e\xdd\xa2\xa3\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x19\xed\xd0\x7b\x16\xaf\xb5\x0c\x9a\xe8\xd3\x46\x2e\x3c\x64\x29\xb6\xc1\x73\x5a\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x19\xed\xd0\x7b\x16\xaf\xb5\x0c\x9a\xe8\xd3\x46\x2e\x3c\x64\x29\xb6\xc1\x73\x5a\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\x62\xa7\x26\xeb\xd4\x03\x29\x9c\x09\x33\x69\x7a\x9c\x65\x68\xec\x4c\xb9\x06\xeb\x1e\x51\x6f\x78\x20\xdc\xf6\x44\x5e\x06\x6e\x53\x87\x73\xe6\x14\x15\xb9\x17\x74\x67\xe0\x4e\x48\x38\xbc\x1c\xbd\xd0\xad\xd6\xbd\x8c\xf0\x3a\xe0\x13\x73\x19\xad\x8b\x79\x68\x67\x65\x9b\x7a\x4c\x81\xfb\xd9\x92\x77\x89\xb5\xb0\x53\xb0\xa5\xf7\x2d\x8e\x29\x60\x31\xd1\x9b\x2f\x63\x8a\x5f\x64\xc1\x61\xd5\xb7\xdf\x70\x3b\x2b\xf6\x1a\x96\xb9\xa7\x08\xca\x87\xa6\x8c\x60\xca\x6e\xd7\xee\xba\xef\x89\x0b\x93\xd5\xfd\xfc\x14\xba\xef\x27\xba\x90\x11\x90\xf7\x25\x70\xe7\x4e\xf4\x9c\x13\x27\xc1\xa7\x8e\xd9\x66\x43\x72\x20\x5b\xe1\x5c\x73\x74\xf5\x33\xf2\xa5\xf6\xe1\xd5\xac\xf3\x67\x5c\xe7\xd4\x0a\x8d\x91\x73\x03\x3e\x9d\xbc\x96\xc3\x0c\xdb\xd5\x77\x6e\x76\x44\x69\xaf\x24\x0f\x4f\x8b\x47\x36\x8b\xc3\xd6\x36\xdd\x26\x5a\x9c\xdd\x9c\x43\xee\x29\x43\xdd\x75\x2f\x19\x52\xfc\x1d\x24\x9c\x13\x29\x99\xa0\x6d\x7a\x95\xcc\xa0\x58\x86\xd8\xc5\xb9\xa3\xc2\x3d\x64\x1d\x85\x8a\xca\x53\x55\x8e\x9a\x6d\xc9\x91\x73\xf4\xe1\xe1\xa4\x9b\x76\xfc\x7f\x63\xc2\xb9\x23"; -static const qbyte updates_triptohell_certdata[962] = "\x30\x82\x03\xbe\x30\x82\x02\xa6\xa0\x03\x02\x01\x02\x02\x09\x00\xf4\x4e\x13\x08\x32\x85\xb5\x19\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x30\x74\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x4b\x31\x10\x30\x0e\x06\x03\x55\x04\x08\x0c\x07\x45\x6e\x67\x6c\x61\x6e\x64\x31\x0f\x30\x0d\x06\x03\x55\x04\x07\x0c\x06\x4c\x6f\x6e\x64\x6f\x6e\x31\x0e\x30\x0c\x06\x03\x55\x04\x0a\x0c\x05\x46\x54\x45\x51\x57\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x55\x70\x64\x61\x74\x65\x73\x31\x20\x30\x1e\x06\x03\x55\x04\x03\x0c\x17\x75\x70\x64\x61\x74\x65\x73\x2e\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x1e\x17\x0d\x31\x39\x30\x35\x33\x31\x31\x30\x30\x39\x31\x39\x5a\x17\x0d\x32\x39\x30\x35\x32\x38\x31\x30\x30\x39\x31\x39\x5a\x30\x74\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x4b\x31\x10\x30\x0e\x06\x03\x55\x04\x08\x0c\x07\x45\x6e\x67\x6c\x61\x6e\x64\x31\x0f\x30\x0d\x06\x03\x55\x04\x07\x0c\x06\x4c\x6f\x6e\x64\x6f\x6e\x31\x0e\x30\x0c\x06\x03\x55\x04\x0a\x0c\x05\x46\x54\x45\x51\x57\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x55\x70\x64\x61\x74\x65\x73\x31\x20\x30\x1e\x06\x03\x55\x04\x03\x0c\x17\x75\x70\x64\x61\x74\x65\x73\x2e\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xaa\xb5\x9c\xcc\xe8\xbd\xad\x1c\x7f\x6b\x1c\xc9\x04\xe6\x2c\x10\xac\x99\xeb\x67\x0b\x9c\x24\xb8\x90\x77\xde\xaa\x44\xa2\x15\x31\x61\x52\x4d\xeb\x8f\x56\xb8\xaf\xc1\x2f\x66\xdd\x55\x8d\xd6\xec\xc3\xa4\x93\x8c\x86\xeb\xaf\x89\x17\x19\x2e\x6c\xc2\xd4\xf9\x92\xac\x2e\x73\x99\x56\xf2\xc3\xc4\x14\x56\x4a\x0d\xbe\x51\xc9\x8f\x4e\x92\x20\x2b\xae\x05\x0c\x7e\x87\xa5\x02\xe1\xc0\x7d\x71\xa7\x38\x72\x47\x3f\x31\x07\x90\xb0\x6d\xcf\xae\xb6\xdb\xeb\x39\xaa\x5f\xb4\x6f\x0c\x63\x2a\x21\x65\x36\xaa\x6b\xac\x97\xb6\xbe\x20\xa4\x87\x36\xbf\x35\xc5\xa6\x31\xe4\x9d\x85\xf3\xae\x8f\x6b\xf8\x59\x75\x0f\xb5\x5d\x31\x40\x39\x2e\xea\x48\x65\xdf\x91\xe3\x06\xfb\xb2\xec\xdc\xd0\x90\x94\xd6\x68\x4d\x62\x25\x9a\x3d\xc3\x74\x17\x7d\x0e\xe2\x1e\x34\xbf\x02\x85\xc4\x40\x88\x91\xeb\xe0\xf5\x92\x56\x42\x4f\xa6\x4c\x17\x88\xb2\x89\xd2\xec\x60\x54\x97\x20\x0a\xca\xf0\xd1\x33\x3f\x5b\x66\xb7\x8a\x42\x72\x67\xc9\x47\x83\xb3\xd4\x1e\xa8\x44\xbf\x5a\x1a\x85\x79\xee\xf8\x80\xde\x19\x1d\xc5\xdd\x50\x42\x10\x17\xb7\xc3\xd4\xf1\xcb\x8a\xb8\x71\x55\x31\x02\x03\x01\x00\x01\xa3\x53\x30\x51\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x2c\x68\x81\x8f\x40\x8c\x40\x42\x9f\xbd\xc5\x0b\x36\xfb\xe2\x76\xeb\x8d\xb4\xf3\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x2c\x68\x81\x8f\x40\x8c\x40\x42\x9f\xbd\xc5\x0b\x36\xfb\xe2\x76\xeb\x8d\xb4\xf3\x30\x0f\x06\x03\x55\x1d\x13\x01\x01\xff\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x0c\x01\x35\x32\xb8\xe7\x96\xba\x3e\x53\x8c\x78\x41\xab\x9b\x7f\xe2\x7a\x80\x5a\xc9\x88\x06\x29\x28\xf9\x50\x7f\xcc\xb6\xcc\x34\x03\x45\x32\x79\x63\xe7\xde\x9c\x46\x29\xf4\xaf\x32\x72\x26\x11\xa0\x7b\x52\x23\x0a\xd5\x51\x91\x79\xf2\x50\x61\x80\x72\x40\xe7\x85\xb0\x13\x1d\x98\xdb\x14\x23\x59\xa4\xbc\xe9\xe0\x1b\xc0\x38\x33\x96\xbc\xbb\x56\x47\xcc\xbd\xe8\x40\x49\xdf\xaa\x64\x7e\x29\xe5\x9d\x40\xa5\x1a\x5c\x45\x1f\x5a\x77\x59\xfe\x7a\xb8\xf8\x4d\xc4\x9b\x31\xe6\x08\xc4\x95\xfa\x91\x8f\x91\x9f\x3c\xc4\x82\xb9\xf1\x6d\xa8\xa6\xc4\x09\xb1\xe9\xa8\x60\x9b\xaa\x4c\x79\xf0\x99\xb8\xad\x63\xb1\xe4\xc0\xaf\xf0\xdf\xc9\x33\x53\x4d\x09\xe4\x3f\x8d\x9e\x38\xc6\x93\xff\xcc\x91\x46\x7e\x67\x28\x61\xaa\xc7\x0b\xe2\xd8\x8c\xe4\xec\x8d\x44\xe7\x6a\x14\x78\x91\x7d\xec\xc7\x07\xed\xc9\x58\xdb\x35\xd4\x70\x06\x06\x39\x8d\x4b\x80\x2c\xb6\xa8\x79\x5c\x94\x15\x6c\x34\x06\x5c\xd7\xc5\x42\xc0\x72\x01\x71\x07\xf5\x25\x6a\xd0\x24\x86\xcd\x1b\x21\x07\xae\x40\xf8\xc1\xe4\x23\x0d\xa0\xc0\x23\xf0\x07\xba\xdc\x34\x5d\x47\xcf\x4b\x7b\xd5\x5d"; -//static const qbyte fte_triptohell_certdata[917] = "\x30\x82\x03\x91\x30\x82\x02\x79\xa0\x03\x02\x01\x02\x02\x09\x00\xb5\x71\x47\x8d\x5e\x66\xf1\xd9\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x30\x5f\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x11\x30\x0f\x06\x03\x55\x04\x07\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x0c\x30\x0a\x06\x03\x55\x04\x0a\x0c\x03\x46\x54\x45\x31\x1c\x30\x1a\x06\x03\x55\x04\x03\x0c\x13\x66\x74\x65\x2e\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x1e\x17\x0d\x31\x34\x31\x32\x32\x35\x30\x30\x35\x38\x31\x34\x5a\x17\x0d\x31\x37\x30\x33\x30\x34\x30\x30\x35\x38\x31\x34\x5a\x30\x5f\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x11\x30\x0f\x06\x03\x55\x04\x07\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x0c\x30\x0a\x06\x03\x55\x04\x0a\x0c\x03\x46\x54\x45\x31\x1c\x30\x1a\x06\x03\x55\x04\x03\x0c\x13\x66\x74\x65\x2e\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xdd\xb8\x7c\x69\x3d\x63\x95\xe3\x88\x15\xfd\xad\x93\x5e\x6b\x97\xfb\x74\xba\x1f\x83\x33\xe5\x8a\x8d\x8f\xb0\xbf\xf9\xd3\xa1\x2c\x65\x53\xa7\xef\xd3\x0f\xdc\x03\x60\x0a\x40\xef\xa8\xef\x3f\xb3\xd9\x8d\x31\x39\x12\x8a\xd8\x0e\x24\x8f\xe5\x58\x26\x86\x4c\x76\x6c\x59\x9a\xab\xea\x1c\x3d\xfb\x62\x62\xad\xaf\xd6\x00\x33\x76\x2d\xbb\xeb\xe8\xec\xb4\x76\x4f\xb0\xbe\xcf\xf0\x46\x94\x40\x02\x99\xd4\xb2\x71\x71\xd6\xf5\x1f\xc3\x4f\x1e\x1e\xb4\x0d\x82\x49\xc4\xa2\xdc\xae\x6f\x4e\x3a\xf9\x0e\xdd\xf4\xd2\x53\xe3\xe7\x7d\x58\x79\xf4\xce\x1f\x6c\xac\x81\x8c\x8c\xe1\x03\x5b\x22\x56\x92\x19\x4f\x74\xc0\x36\x41\xac\x1b\xfa\x9e\xf7\x2a\x0f\xd6\x4b\xcc\x9a\xca\x67\x87\xb7\x95\xdf\xb7\xd4\x7d\x8c\xcc\xa9\x25\xde\xdd\x8c\x1b\xd7\x32\xf2\x84\x25\x46\x7b\x10\x55\xf9\x80\xfd\x5d\xad\xab\xf9\x4c\x1f\xc0\xa5\xd1\x3f\x01\x86\x4d\xfa\x57\xab\x7a\x6d\xec\xf1\xdb\xf4\xad\xf2\x33\xcd\xa0\xed\xfe\x1b\x27\x55\x56\xba\x8c\x47\x70\x16\xd5\x75\x17\x8e\x80\xaa\x49\x5e\x93\x83\x1d\x6f\x1f\x2c\xf7\xa7\x64\xe6\x2e\x88\x8e\xff\x70\x5a\x41\x52\xae\x93\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x4e\x76\x4a\xce\x7b\x45\x14\x39\xeb\x9c\x28\x56\xb5\x7b\x8a\x18\x6f\x22\x17\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x4e\x76\x4a\xce\x7b\x45\x14\x39\xeb\x9c\x28\x56\xb5\x7b\x8a\x18\x6f\x22\x17\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x48\x22\x65\xed\x2e\xc5\xed\xbb\xe9\x40\x6c\x80\xc4\x63\x19\xd1\x00\xb4\x30\x34\x17\x7c\x7c\xbd\x1b\xc5\xa9\x43\x0c\x92\x6e\xd6\x2d\x11\x6c\x0d\xa6\xda\x30\xe9\xf7\x46\x7b\x01\xe4\x53\x23\xae\x88\xd1\xf2\xed\xca\x84\x06\x19\x97\xb9\x06\xfb\xda\xec\x72\x2d\x15\x20\xd2\x8f\x66\xad\xb5\xdd\x4b\x4f\xdf\x7e\xaf\xa3\x6c\x7f\x53\x32\x8f\xe2\x19\x5c\x44\x98\x86\x31\xee\xb4\x03\xe7\x27\xa1\x83\xab\xc3\xce\xb4\x9a\x01\xbe\x8c\x64\x2e\x2b\xe3\x4e\x55\xdf\x95\xeb\x16\x87\xbd\xfa\x11\xa2\x3e\x38\x92\x97\x36\xe9\x65\x60\xf3\xac\x68\x44\xb3\x51\x54\x3a\x42\xa8\x98\x9b\xee\x1b\x9e\x79\x6a\xaf\xc0\xbe\x41\xc4\xb1\x96\x42\xd9\x94\xef\x49\x5b\xbe\x2d\x04\xb9\xfb\x92\xbb\xdc\x0e\x29\xfd\xee\xa9\x68\x09\xf9\x9f\x69\x8b\x3d\xe1\x4b\xee\x24\xf9\xfe\x02\x3a\x0a\xb8\xcd\x6c\x07\x43\xa9\x4a\xe7\x03\x34\x2e\x72\xa7\x81\xaa\x40\xa9\x98\x5d\x97\xee\x2a\x99\xc6\x8f\xe8\x6f\x98\xa2\x85\xc9\x0d\x04\x19\x43\x6a\xd3\xc7\x15\x4c\x4b\xbc\xa5\xb8\x9f\x38\xf3\x43\x83\x0c\xef\x97\x6e\xa6\x20\xde\xc5\xd3\x1e\x3e\x5d\xcd\x58\x3d\x5c\x55\x7a\x90\x94"; -//static const qbyte triptohell_certdata[933] = "\x30\x82\x03\xa1\x30\x82\x02\x89\xa0\x03\x02\x01\x02\x02\x09\x00\xea\xb7\x13\xcf\x55\xe5\xe8\x8c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x30\x67\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x11\x30\x0f\x06\x03\x55\x04\x07\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x18\x30\x16\x06\x03\x55\x04\x0a\x0c\x0f\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x31\x18\x30\x16\x06\x03\x55\x04\x03\x0c\x0f\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x1e\x17\x0d\x31\x34\x31\x32\x32\x35\x30\x30\x35\x38\x33\x37\x5a\x17\x0d\x31\x37\x30\x33\x30\x34\x30\x30\x35\x38\x33\x37\x5a\x30\x67\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x11\x30\x0f\x06\x03\x55\x04\x07\x0c\x08\x4e\x65\x77\x20\x59\x6f\x72\x6b\x31\x18\x30\x16\x06\x03\x55\x04\x0a\x0c\x0f\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x31\x18\x30\x16\x06\x03\x55\x04\x03\x0c\x0f\x74\x72\x69\x70\x74\x6f\x68\x65\x6c\x6c\x2e\x69\x6e\x66\x6f\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xd8\x77\x62\xf6\x74\xa7\x75\xde\xda\x09\xae\x9e\x76\x7a\xc6\x2a\xcf\x9a\xbe\xc6\xb9\x6d\xe2\xca\x0f\x2d\x95\xb8\x89\x93\xf7\x50\x64\x92\x7d\x95\x34\xe4\x6e\xef\x52\x56\xef\x13\x9a\x3a\xae\x84\x5b\x57\x82\x04\x86\x74\xbd\x4e\x38\x32\x56\x00\xd6\x34\x9c\x23\xd6\x81\x8e\x29\x77\x45\x61\x20\xdf\x28\xf8\xe5\x61\x83\xec\xe6\xa0\x1a\x75\xa8\x3b\x53\x6f\xc4\x09\x61\x66\x3a\xf0\x81\xbf\x2c\xf5\x8e\xf1\xe2\x35\xe4\x24\x7f\x16\xcc\xce\x60\xa2\x42\x6e\xc2\x3a\x29\x75\x6c\x79\xb0\x99\x9c\xe2\xfe\x27\x32\xb6\xf7\x0d\x71\xfd\x62\x9d\x54\x7c\x40\xb2\xf5\xa0\xa4\x25\x31\x8d\x65\xfd\x3f\x3b\x9b\x7e\x84\x74\x17\x3c\x1f\xec\x50\xcf\x75\xb8\x5c\xca\xfc\x0f\xe8\x47\xd8\x64\xec\x5f\x6c\x45\x9a\x55\x49\x97\x3f\xcb\x49\x34\x71\x0a\x12\x13\xbc\x3d\x53\x81\x17\x9a\x92\x44\x91\x07\xc2\xef\x6d\x64\x86\x5d\xfd\x67\xd5\x99\x38\x95\x46\x74\x6d\xb6\xbf\x29\xc9\x5b\xac\xb1\x46\xd6\x9e\x57\x5c\x7b\x24\x91\xf4\x7c\xe4\x01\x31\x8c\xec\x79\x94\xb7\x3f\xd2\x93\x6d\xe2\x69\xbe\x61\x44\x2e\x8f\x1a\xdc\xa8\x97\xf5\x81\x8e\x0c\xe1\x00\xf2\x71\x51\xf3\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x18\xb2\x6b\x63\xcc\x17\x54\xf6\xf0\xb6\x9e\x62\xa4\x35\xcf\x47\x74\x13\x29\xbf\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x18\xb2\x6b\x63\xcc\x17\x54\xf6\xf0\xb6\x9e\x62\xa4\x35\xcf\x47\x74\x13\x29\xbf\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x7f\x24\x18\x8a\x79\xee\xf9\xeb\xed\x29\x1e\x21\x15\x8a\x53\xc9\xb7\xec\x30\xc4\x85\x9f\x45\x85\x26\x36\xb7\x07\xf3\xf1\xff\x3b\x89\x05\x0a\xd4\x30\x68\x31\x68\x33\xdd\xf6\x58\xa3\x85\x9f\x49\x50\x76\x9a\xc5\x79\x13\xe1\x4d\x67\x0c\xf3\x92\xf0\x1d\x02\x1f\xc4\x5c\xd4\xa1\x0c\x57\xdf\x46\x84\x43\x9f\xb0\xe2\x91\x62\xa8\xe0\x86\x0d\x47\xe1\xd9\x60\x01\xc4\xe0\xda\x6f\x06\x0a\xad\x38\xf3\x66\x68\xc5\xe2\x66\x3e\x47\x83\x65\x64\xcd\xff\xf3\xbb\xa7\xfa\x23\xf1\x82\x5e\x06\x6a\x91\x37\x51\xcd\xb9\x95\x20\x89\xff\xa1\x54\xb2\x76\xcf\x8e\xe1\xcd\x13\x93\x13\xd1\xda\x0d\x0d\xbc\x0f\xd5\x11\x26\xd6\xaf\x60\x0f\x4d\x8a\x4f\x28\xee\x6c\xf1\x99\xdc\xed\x16\xdc\x87\x26\xfd\x23\x8a\xb8\xb0\x20\x0e\xe2\x32\xf5\x8e\xb0\x65\x98\x13\xb8\x4b\x39\x7c\x8c\x98\xa2\x29\x75\x48\x3a\x89\xf9\x61\x77\x6c\x2d\x84\x41\x40\x17\xa6\x50\xc5\x09\x63\x10\xe7\x09\xd4\x5c\xdd\x0e\x71\x16\xaf\xb1\x32\xe4\xc0\xe6\xea\xfd\x26\x55\x07\x40\x95\x84\x48\x62\x04\x10\x92\xb2\xd9\x27\xfb\x8a\xf3\x7c\xe6\xfe\xd4\xfc\xa6\x33\x79\x01\x5c\xc3\x1f\x80\xa8\xf3"; -static struct -{ - wchar_t *hostname; - unsigned int datasize; - const qbyte *data; - //FIXME: include expiry information - //FIXME: add alternative when one is about to expire -} knowncerts[] = { -// {L"triptohell.info", sizeof(triptohell_certdata), triptohell_certdata}, -// {L"fte.triptohell.info", sizeof(fte_triptohell_certdata), fte_triptohell_certdata}, - {L"updates.triptohell.info", sizeof(updates_triptohell_certdata), updates_triptohell_certdata}, - {NULL} -}; - char *narrowen(char *out, size_t outlen, wchar_t *wide); static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, size_t datasize, qboolean datagram) { - int i; -#ifndef SERVERONLY + size_t knownsize; + void *knowncert; char realdomain[256]; -#endif if (datagram) { if (status == CERT_E_UNTRUSTEDROOT || SUCCEEDED(status)) @@ -423,48 +404,31 @@ static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, } return status; } - for (i = 0; knowncerts[i].hostname; i++) + + narrowen(realdomain, sizeof(realdomain), domain); + knowncert = TLS_GetKnownCertificate(realdomain, &knownsize); + if (knowncert) { - if (!wcscmp(domain, knowncerts[i].hostname)) - { -#ifdef _DEBUG - if (!knowncerts[i].data) - { - int j; - Con_Printf("%ls cert %i bytes\n", domain, (int)datasize); - - Con_Printf("\""); - for (j = 0; j < datasize; j++) - Con_Printf("\\x%02x", data[j]); - Con_Printf("\"\n"); - - Con_Printf("\n"); - for (j = 0; j < datasize; j++) - Con_Printf("%c", data[j]); - continue; - } -#endif - if (knowncerts[i].datasize == datasize && !memcmp(data, knowncerts[i].data, datasize)) - { //what we know about matched - if (status == CERT_E_UNTRUSTEDROOT) - status = SEC_E_OK; - break; - } - else - { - if (status != CERT_E_EXPIRED) - Con_Printf("%ls has an unexpected certificate\n", domain); - if (status == SEC_E_OK) //we (think) we know better. - status = TRUST_E_FAIL; - } + if (knownsize == datasize && !memcmp(data, knowncert, datasize)) + { //what we know about matched + if (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_EXPIRED) + status = SEC_E_OK; } + else + { + if (status != CERT_E_EXPIRED) + Con_Printf("%ls has an unexpected certificate\n", domain); + if (status == SEC_E_OK) //we (think) we know better. + status = TRUST_E_FAIL; + } + BZ_Free(knowncert); } #ifndef SERVERONLY //self-signed and expired certs are understandable in many situations. //prompt and cache (although this connection attempt will fail). if (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_UNTRUSTEDTESTROOT || status == CERT_E_EXPIRED) - if (CertLog_ConnectOkay(narrowen(realdomain, sizeof(realdomain), domain), data, datasize)) + if (CertLog_ConnectOkay(realdomain, data, datasize)) return SEC_E_OK; #endif diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index a4b7e512..baa2db7a 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -1053,8 +1053,19 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin //its not meant to be used for browsers etc, and I cba to register dns stuff for it. //besides, browsers/etc would just bitch about its cert, so w/e. //redirect the dns to the base host without affecting http(s) hosts/certificates. - if (!strcmp(s, "updates.triptohell.info")) - s += 8; + if (!strncmp(s, "updates.", 8)) + { + conchar_t musite[256], *e; + char site[256]; + char *oldprefix = "http://fte."; + e = COM_ParseFunString(CON_WHITEMASK, ENGINEWEBSITE, musite, sizeof(musite), false); + COM_DeFunString(musite, e, site, sizeof(site), true, true); + if (!strncmp(site, oldprefix, strlen(oldprefix))) + { + if (!strcmp(s+8,site+strlen(oldprefix))) + s += 8; + } + } #endif memset (sadr, 0, sizeof(*sadr)); @@ -2111,6 +2122,52 @@ qboolean NET_IsLoopBackAddress (netadr_t *adr) #ifdef HAVE_SSL +void *TLS_GetKnownCertificate(const char *certname, size_t *size) +{ + //Note: This is XORed because of shitty scanners flagging binaries through false positive, flagging the sites that they were downloaded from, flagging binaries that contain references to those sites, and flagging any site that contains binaries. + //the xor helps break that shitty recursive loop of mistrust from defects in other people's code. + //at least until there's a sandbox that checks the dns resolutions for our update requests anyway. + //I should probably just copy the downloadables file to sourceforge. + static struct + { + qbyte *data; + } knowncerts[] = { + { + /*updates.triptohell.info*/"\x8a\x8f\x9b\x9e\x8b\x9a\x8c\xd1\x8b\x8d\x96\x8f\x8b\x90\x97\x9a\x93\x93\xd1\x96\x91\x99\x90\xff\xc2\x03\xcf\x7d\xfc\x41\xcf\x7d\xfd\x59\x5f\xfc\xfd\xfe\xfd\xfd\xf6\xff\x0b\xb1\xec\xf7\xcd\x7a\x4a\xe6\xcf\xf2\xf9\xf6\xd5\x79\xb7\x79\x08\xf2\xfe\xfe\xf4\xfa\xff\xcf\x8b\xce\xf4\xcf\xf6\xf9\xfc\xaa\xfb\xf9\xec\xfd\xaa\xb4\xce\xef\xcf\xf1\xf9\xfc\xaa\xfb\xf7\xf3\xf8\xba\x91\x98\x93\x9e\x91\x9b\xce\xf0\xcf\xf2\xf9\xfc\xaa\xfb\xf8\xf3\xf9\xb3\x90\x91\x9b\x90\x91\xce\xf1\xcf\xf3\xf9\xfc\xaa\xfb\xf5\xf3\xfa\xb9\xab\xba\xae\xa8\xce\xef\xcf\xf1\xf9\xfc\xaa\xfb\xf4\xf3\xf8\xaa\x8f\x9b\x9e\x8b\x9a\x8c\xce\xdf\xcf\xe1\xf9\xfc\xaa\xfb\xfc\xf3\xe8\x8a\x8f\x9b\x9e\x8b\x9a\x8c\xd1\x8b\x8d\x96\x8f\x8b\x90\x97\x9a\x93\x93\xd1\x96\x91\x99\x90\xcf\xe1\xe8\xf2\xce\xc6\xcf\xca\xcc\xce\xce\xcf\xcf\xc6\xce\xc6\xa5\xe8\xf2\xcd\xc6\xcf\xca\xcd\xc7\xce\xcf\xcf\xc6\xce\xc6\xa5\xcf\x8b\xce\xf4\xcf\xf6\xf9\xfc\xaa\xfb\xf9\xec\xfd\xaa\xb4\xce\xef\xcf\xf1\xf9\xfc\xaa\xfb\xf7\xf3\xf8\xba\x91\x98\x93\x9e\x91\x9b\xce\xf0\xcf\xf2\xf9\xfc\xaa\xfb\xf8\xf3\xf9\xb3\x90\x91\x9b\x90\x91\xce\xf1\xcf\xf3\xf9\xfc\xaa\xfb\xf5\xf3\xfa\xb9\xab\xba\xae\xa8\xce\xef\xcf\xf1\xf9\xfc\xaa\xfb\xf4\xf3\xf8\xaa\x8f\x9b\x9e\x8b\x9a\x8c\xce\xdf\xcf\xe1\xf9\xfc\xaa\xfb\xfc\xf3\xe8\x8a\x8f\x9b\x9e\x8b\x9a\x8c\xd1\x8b\x8d\x96\x8f\x8b\x90\x97\x9a\x93\x93\xd1\x96\x91\x99\x90\xcf\x7d\xfe\xdd\xcf\xf2\xf9\xf6\xd5\x79\xb7\x79\x08\xf2\xfe\xfe\xfe\xfa\xff\xfc\x7d\xfe\xf0\xff\xcf\x7d\xfe\xf5\xfd\x7d\xfe\xfe\xff\x55\x4a\x63\x33\x17\x42\x52\xe3\x80\x94\xe3\x36\xfb\x19\xd3\xef\x53\x66\x14\x98\xf4\x63\xdb\x47\x6f\x88\x21\x55\xbb\x5d\xea\xce\x9e\xad\xb2\x14\x70\xa9\x47\x50\x3e\xd0\x99\x22\xaa\x72\x29\x13\x3c\x5b\x6c\x73\x79\x14\x50\x76\xe8\xe6\xd1\x93\x3d\x2b\x06\x6d\x53\xd1\x8c\x66\xa9\x0d\x3c\x3b\xeb\xa9\xb5\xf2\x41\xae\x36\x70\xb1\x6d\xdf\xd4\x51\xfa\xf3\x81\x78\x5a\xfd\x1e\x3f\x82\x8e\x58\xc7\x8d\xb8\xc0\xce\xf8\x6f\x4f\x92\x30\x51\x49\x24\x14\xc6\x55\xa0\x4b\x90\xf3\x9c\xd5\xde\x9a\xc9\x55\x94\x53\x68\x49\x41\xdf\x5b\x78\xc9\x40\xca\x3a\x59\xce\x1b\x62\x7a\x0c\x51\x70\x94\x07\xa6\x8a\xf0\x4a\xa2\xce\xbf\xc6\xd1\x15\xb7\x9a\x20\x6e\x1c\xf9\x04\x4d\x13\x23\x2f\x6f\x6b\x29\x97\xb2\x9d\xda\x65\xc2\x3c\x8b\xe8\x82\xf1\x1d\xe1\xcb\x40\xfd\x7a\x3b\xbf\x77\x6e\x14\x1f\x0a\x6d\xa9\xbd\xb0\x59\xb3\xe8\x77\x4d\x76\x2d\x13\x9f\xab\x68\xdf\xf5\x35\x0f\x2e\xcc\xc0\xa4\x99\x48\x75\xbd\x8d\x98\x36\xb8\x7c\x4c\x2b\xe1\x57\xbb\x40\xa5\xe5\x7a\x86\x11\x07\x7f\x21\xe6\xe2\x3a\x22\xaf\xbd\xef\xe8\x48\x3c\x2b\x0e\x34\x75\x47\x8e\xaa\xce\xfd\xfc\xfe\xff\xfe\x5c\xac\xcf\xae\xcf\xe2\xf9\xfc\xaa\xe2\xf1\xfb\xe9\xfb\xeb\xd3\x97\x7e\x70\xbf\x73\xbf\xbd\x60\x42\x3a\xf4\xc9\x04\x1d\x89\x14\x72\x4b\x0c\xcf\xe0\xf9\xfc\xaa\xe2\xdc\xfb\xe7\xcf\xe9\x7f\xeb\xd3\x97\x7e\x70\xbf\x73\xbf\xbd\x60\x42\x3a\xf4\xc9\x04\x1d\x89\x14\x72\x4b\x0c\xcf\xf0\xf9\xfc\xaa\xe2\xec\xfe\xfe\x00\xfb\xfa\xcf\xfc\xfe\xfe\x00\xcf\xf2\xf9\xf6\xd5\x79\xb7\x79\x08\xf2\xfe\xfe\xf4\xfa\xff\xfc\x7d\xfe\xfe\xff\xf3\xfe\xca\xcd\x47\x18\x69\x45\xc1\xac\x73\x87\xbe\x54\x64\x80\x1d\x85\x7f\xa5\x36\x77\xf9\xd6\xd7\x06\xaf\x80\x33\x49\x33\xcb\xfc\xba\xcd\x86\x9c\x18\x21\x63\xb9\xd6\x0b\x50\xcd\x8d\xd9\xee\x5f\x84\xad\xdc\xf5\x2a\xae\x6e\x86\x0d\xaf\x9e\x7f\x8d\xbf\x18\x7a\x4f\xec\xe2\x67\x24\xeb\xdc\xa6\x5b\x43\x16\x1f\xe4\x3f\xc7\xcc\x69\x43\x44\xa9\xb8\x33\x42\x17\xbf\xb6\x20\x55\x9b\x81\xd6\x1a\x62\xbf\x5a\xe5\xa3\xba\xe0\xa5\x88\xa6\x01\x85\x47\x07\xb2\x3b\x64\xce\x19\xf7\x3b\x6a\x05\x6e\x70\x6e\x60\xc3\x3b\x7d\x46\x0e\x92\x57\x59\x3b\xf6\x4e\x16\x57\x9f\x64\x55\xb3\x86\x0f\x66\x47\x52\x9c\x4e\x1b\x3f\x50\x0f\x20\x36\xcc\xac\xb2\xf6\x1b\xc0\x72\x61\xc7\x39\x6c\x00\x33\x6e\xb9\x81\x98\xd7\x9e\x55\x38\xf4\x1d\x27\x73\x1b\x13\x72\xbb\x18\x95\xeb\x87\x6e\x82\x13\x38\xf8\x12\x36\xa7\x24\xca\x2b\x8f\xf9\xf9\xc6\x72\xb4\x7f\xd3\x49\x57\x86\xa3\x6b\xea\x93\xcb\xf9\xa3\x28\x3a\xbd\x3f\x8d\xfe\x8e\xf8\x0a\xda\x95\x2f\xdb\x79\x32\xe4\xde\xf8\x51\xbf\x07\x3e\x1b\xdc\xf2\x5f\x3f\xdc\x0f\xf8\x45\x23\xcb\xa2\xb8\x30\xb4\x84\x2a\xa2", + }, + }; + + qbyte *r, *t; + size_t i, j, sz; + + for (i = 0; i < countof(knowncerts); i++) + { + t = knowncerts[i].data; + for (j = 0; ; j++) + { + if (certname[j] != (t[j]^0xff)) + break; + if (!certname[j]) + { + j++; + t+=j; + sz = t[0] | (t[1]<<8); + t += 2; + + r = BZ_Malloc(sz); + *size = sz; + while(sz --> 0) + r[sz] = t[sz]^0xff; + return r; + } + } + } + + *size = 0; + //return Z_StrDup(""); //to force failure... gnutls debug code will dump out a cert that can be inserted above. + return NULL; +} + vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean isserver) { vfsfile_t *f = NULL; @@ -4058,7 +4115,7 @@ qboolean FTENET_TCPConnect_HTTPResponse(ftenet_tcpconnect_stream_t *st, httparg_ ; #endif #ifdef HAVE_SERVER - else if (!strcmp(name, "index.html")) + /*else if (!strcmp(name, "index.html")) { resp = "HTTP/1.1 200 Ok\r\n" "Content-Type: text/html\r\n"; @@ -4069,11 +4126,7 @@ qboolean FTENET_TCPConnect_HTTPResponse(ftenet_tcpconnect_stream_t *st, httparg_ "" "" "" -#ifdef _WIN32 "" -#else - "" -#endif "%s - %s" "" +#ifdef EMBEDGAME "" +#endif "" "" "
\n" @@ -2588,7 +2591,11 @@ void SV_UserCmdMVDList_HTML (vfsfile_t *pipe) { char datetime[64]; strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", localtime(&list->mtime)); +#ifdef EMBEDGAME VFS_PRINTF(pipe, "%d: %s %uk play %s
\n", i, list->name, list->name, (unsigned int)(list->size/1024), list->name, datetime); +#else + VFS_PRINTF(pipe, "%d: %s %uk %s
\n", i, list->name, list->name, (unsigned int)(list->size/1024), datetime); +#endif } } @@ -2606,13 +2613,15 @@ void SV_UserCmdMVDList_HTML (vfsfile_t *pipe) VFS_PRINTF(pipe, "
" +#ifdef EMBEDGAME "
" "" + "" "
" +#endif "\n" "\n"); diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 125aefa6..8aa0d42f 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -464,7 +464,7 @@ static int WPhys_FlyMove (world_t *w, wedict_t *ent, const vec3_t gravitydir, fl if (-DotProduct(gravitydir, trace.plane.normal) > 0.7) { blocked |= 1; // floor - if (((wedict_t *)trace.ent)->v->solid == SOLID_BSP) + if (((wedict_t *)trace.ent)->v->solid == SOLID_BSP || dpcompat_noretouchground.ival) { ent->v->flags = (int)ent->v->flags | FL_ONGROUND; ent->v->groundentity = EDICT_TO_PROG(w->progs, trace.ent); diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index c4a761f5..33dd57a4 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -699,8 +699,6 @@ static void Friendly_Crash_Handler(int sig, siginfo_t *info, void *vcontext) strftime (buffer, sizeof(buffer), "Time: %Y-%m-%d %H:%M:%S\n",timeinfo); write(fd, buffer, strlen(buffer)); - Q_snprintfz(buffer, sizeof(buffer), "Binary: "__DATE__" "__TIME__"\n"); - write(fd, buffer, strlen(buffer)); Q_snprintfz(buffer, sizeof(buffer), "Ver: %i.%02i%s\n", FTE_VER_MAJOR, FTE_VER_MINOR, #ifdef OFFICIAL_RELEASE " (official)"); @@ -708,13 +706,17 @@ static void Friendly_Crash_Handler(int sig, siginfo_t *info, void *vcontext) ""); #endif write(fd, buffer, strlen(buffer)); -#ifdef SVNREVISION - if (strcmp(STRINGIFY(SVNREVISION), "-")) - { - Q_snprintfz(buffer, sizeof(buffer), "Revision: %s\n", STRINGIFY(SVNREVISION)); - write(fd, buffer, strlen(buffer)); - } + +#if defined(SVNREVISION) && defined(SVNDATE) + Q_snprintfz(buffer, sizeof(buffer), "Revision: %s\nBinary: %s\n", STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE)); +#else + Q_snprintfz(buffer, sizeof(buffer), + #ifdef SVNREVISION + "Revision: "STRINGIFY(SVNREVISION)"\n" + #endif + "Binary: "__DATE__" "__TIME__"\n"); #endif + write(fd, buffer, strlen(buffer)); backtrace_symbols_fd(array + firstframe, size - firstframe, fd); write(fd, "\n", 1); diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 7277c71d..69883e8d 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -304,7 +304,7 @@ void SV_New_f (void) fteext1 |= PEXT_FLOATCOORDS; if (!(host_client->fteprotocolextensions & PEXT_FLOATCOORDS)) { - SV_ClientPrintf(host_client, 2, "\n\n\n\nPlease set cl_nopext to 0 and then reconnect.\nIf that doesn't work, please update your engine - "ENGINEWEBSITE"\n"); + SV_ClientPrintf(host_client, 2, "\n\n\n\nPlease set cl_nopext to 0 and then reconnect.\nIf that doesn't work, please update your engine\n"); Con_Printf("%s does not support bigcoords\n", host_client->name); host_client->drop = true; return; @@ -663,13 +663,10 @@ void SVNQ_New_f (void) #ifdef OFFICIAL_RELEASE Q_snprintfz(build, sizeof(build), "v%i.%02i", FTE_VER_MAJOR, FTE_VER_MINOR); +#elif defined(SVNREVISION) + Q_snprintfz(build, sizeof(build), "SVN %s", STRINGIFY(SVNREVISION)); #else -#if defined(SVNREVISION) - if (strcmp(STRINGIFY(SVNREVISION), "-")) - Q_snprintfz(build, sizeof(build), "SVN %s", STRINGIFY(SVNREVISION)); - else -#endif - Q_snprintfz(build, sizeof(build), "%s", __DATE__); + Q_snprintfz(build, sizeof(build), "%s", __DATE__); #endif gamedir = InfoBuf_ValueForKey (&svs.info, "*gamedir"); @@ -2033,7 +2030,10 @@ void SV_Begin_Core(client_t *split) if (eval) { char buf[256]; - svprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, NET_AdrToString(buf, sizeof(buf), &split->netchan.remote_address), false); + if (split->netchan.remote_address.type == NA_LOOPBACK) + svprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, "local", false); //sigh... + else + svprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, NET_AdrToString(buf, sizeof(buf), &split->netchan.remote_address), false); } } #endif @@ -2684,7 +2684,7 @@ void SV_VoiceReadPacket(void) bytes = MSG_ReadShort(); ring = &voice.ring[voice.write & (VOICE_RING_SIZE-1)]; //voice data does not get echoed to the sender unless sv_voip_echo is on too, which is rarely the case, so no worries about leaking the mute+deaf talking-to-yourself thing - if (bytes > sizeof(ring->data) || (host_client->penalties & BAN_MUTE) || !sv_voip.ival) + if (bytes > sizeof(ring->data) || (host_client->penalties & (BAN_MUTE|BAN_VMUTE)) || !sv_voip.ival) { MSG_ReadSkip(bytes); return; diff --git a/engine/server/world.c b/engine/server/world.c index 8df50f0d..c4ec7d19 100644 --- a/engine/server/world.c +++ b/engine/server/world.c @@ -509,6 +509,9 @@ SV_LinkEdict */ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers) { + vec_t *mins; + vec_t *maxs; + #ifdef USEAREAGRID World_UnlinkEdict (ent); // unlink from old position #else @@ -524,11 +527,31 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers) if (ED_ISFREE(ent)) return; + mins = ent->v->mins; + maxs = ent->v->maxs; + if (!ent->v->solid) + ent->solidsize = ES_SOLID_BSP; + else + { + model_t *mod; + if (ent->v->solid == SOLID_BSP) + mod = w->Get_CModel(w, ent->v->modelindex); + else + mod = NULL; + if (mod && mod->type == mod_brush) + { + mins = mod->mins; + maxs = mod->maxs; + ent->solidsize = ES_SOLID_BSP; + } + else + ent->solidsize = COM_EncodeSize(mins, maxs); + } + // set the abs box if (ent->v->solid == SOLID_BSP && (ent->v->angles[0] || ent->v->angles[1] || ent->v->angles[2]) ) { // expand for rotation - #if 1 int i; float v; @@ -537,10 +560,10 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers) max = 0; for (i=0 ; i<3 ; i++) { - v =fabs( ent->v->mins[i]); + v =fabs( mins[i]); if (v > max) max = v; - v =fabs( ent->v->maxs[i]); + v =fabs( maxs[i]); if (v > max) max = v; } @@ -560,13 +583,13 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers) AngleVectors(ent->v->angles, f,r,u); - mn[0] = DotProduct(ent->v->mins, f); - mn[1] = -DotProduct(ent->v->mins, r); - mn[2] = DotProduct(ent->v->mins, u); + mn[0] = DotProduct(mins, f); + mn[1] = -DotProduct(mins, r); + mn[2] = DotProduct(mins, u); - mx[0] = DotProduct(ent->v->maxs, f); - mx[1] = -DotProduct(ent->v->maxs, r); - mx[2] = DotProduct(ent->v->maxs, u); + mx[0] = DotProduct(maxs, f); + mx[1] = -DotProduct(maxs, r); + mx[2] = DotProduct(maxs, u); for (i = 0; i < 3; i++) { if (mn[i] < mx[i]) @@ -584,8 +607,8 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers) } else { - VectorAdd (ent->v->origin, ent->v->mins, ent->v->absmin); - VectorAdd (ent->v->origin, ent->v->maxs, ent->v->absmax); + VectorAdd (ent->v->origin, mins, ent->v->absmin); + VectorAdd (ent->v->origin, maxs, ent->v->absmax); } //some fancy things can mean the ent's aabb is larger than its collision box. @@ -598,21 +621,6 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers) skel_updateentbounds(w, ent); #endif - if (!ent->v->solid) - ent->solidsize = ES_SOLID_BSP; - else - { - model_t *mod; - if (ent->v->solid == SOLID_BSP) - mod = w->Get_CModel(w, ent->v->modelindex); - else - mod = NULL; - if (mod && mod->type == mod_brush) - ent->solidsize = ES_SOLID_BSP; - else - ent->solidsize = COM_EncodeSize(ent->v->mins, ent->v->maxs); - } - // // to make items easier to pick up and allow them to be grabbed off // of shelves, the abs sizes are expanded diff --git a/engine/shaders/glsl/defaultwall.glsl b/engine/shaders/glsl/defaultwall.glsl index 104a5e6e..c71cf930 100644 --- a/engine/shaders/glsl/defaultwall.glsl +++ b/engine/shaders/glsl/defaultwall.glsl @@ -7,7 +7,9 @@ !!permu BUMP !!permu SPECULAR !!permu REFLECTCUBEMASK +!!permu FAKESHADOWS !!cvarf r_glsl_offsetmapping_scale +!!cvardf r_glsl_pcf !!cvardf r_tessellation_level=5 !!samps diffuse !!samps !EIGHTBIT =FULLBRIGHT fullbright @@ -20,6 +22,7 @@ !!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 !!samps =DELUXE deluxmap !!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 +!!samps =FAKESHADOWS shadowmap #if defined(ORM) || defined(SG) #define PBR @@ -53,6 +56,10 @@ varying vec2 lm0; #endif #endif + + #ifdef FAKESHADOWS + varying vec4 vtexprojcoord; + #endif #endif #ifdef VERTEX_SHADER @@ -89,12 +96,18 @@ void main () lm3 = v_lmcoord4; #endif #endif - gl_Position = ftetransform(); #ifdef TESS vertex = v_position; normal = v_normal; #endif + +#ifdef FAKESHADOWS + gl_Position = ftetransform(); + vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); +#else + gl_Position = ftetransform(); +#endif } #endif @@ -239,6 +252,7 @@ void main() #define s_colourmap s_t0 #include "sys/pbr.h" +#include "sys/pcf.h" #ifdef OFFSETMAPPING #include "sys/offsetmapping.h" @@ -305,7 +319,7 @@ void main () vec3 deluxe = (texture2D(s_deluxemap, lm0).rgb-0.5); #ifdef BUMPMODELSPACE deluxe = normalize(deluxe*invsurface); - #else + #else deluxe = normalize(deluxe); lightmaps *= 2.0 / max(0.25, deluxe.z); //counter the darkening from deluxemaps #endif @@ -385,6 +399,12 @@ void main () //entity modifiers col *= e_colourident; +#ifdef FAKESHADOWS + /*filter the light by the shadowmap. logically a boolean, but we allow fractions for softer shadows*/ + col.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); + //col.g = ShadowmapFilter(s_shadowmap, vtexprojcoord); +#endif + #if defined(MASK) #if defined(MASKLT) if (col.a < MASK) diff --git a/engine/shaders/vulkan/defaultwall.glsl b/engine/shaders/vulkan/defaultwall.glsl index 079607ce..9a018fc3 100644 --- a/engine/shaders/vulkan/defaultwall.glsl +++ b/engine/shaders/vulkan/defaultwall.glsl @@ -6,7 +6,7 @@ !!cvarf gl_specular=0.3 !!cvarb r_fog_exp2=true !!samps diffuse normalmap specular fullbright lightmap -!!samps deluxmap reflectmask reflectcube +!!samps deluxemap reflectmask reflectcube !!argb vertexlit=0 //!!samps =EIGHTBIT paletted 1 //!!argb eightbit=0 @@ -101,10 +101,10 @@ void main () #ifdef LIGHTSTYLED if (DELUXE) { - lightmaps = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_deluxmap0, lm0).rgb-0.5); - lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb * dot(norm, 2.0*texture2D(s_deluxmap1, lm1).rgb-0.5); - lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb * dot(norm, 2.0*texture2D(s_deluxmap2, lm2).rgb-0.5); - lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb * dot(norm, 2.0*texture2D(s_deluxmap3, lm3).rgb-0.5); + lightmaps = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_deluxemap0, lm0).rgb-0.5); + lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb * dot(norm, 2.0*texture2D(s_deluxemap1, lm1).rgb-0.5); + lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb * dot(norm, 2.0*texture2D(s_deluxemap2, lm2).rgb-0.5); + lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb * dot(norm, 2.0*texture2D(s_deluxemap3, lm3).rgb-0.5); } else { @@ -127,7 +127,7 @@ void main () //modulate by the bumpmap dot light if (DELUXE) { - vec3 delux = 2.0*(texture2D(s_deluxmap, lm0).rgb-0.5); + vec3 delux = 2.0*(texture2D(s_deluxemap, lm0).rgb-0.5); lightmaps *= 1.0 / max(0.25, delux.z); //counter the darkening from deluxmaps lightmaps *= dot(norm, delux); } @@ -142,7 +142,7 @@ void main () if (DELUXE) { //not lightstyled... - halfdir = normalize(normalize(eyevector) + 2.0*(texture2D(s_deluxmap0, lm0).rgb-0.5)); //this norm should be the deluxemap info instead + halfdir = normalize(normalize(eyevector) + 2.0*(texture2D(s_deluxemap0, lm0).rgb-0.5)); //this norm should be the deluxemap info instead } else { diff --git a/engine/vk/vk_backend.c b/engine/vk/vk_backend.c index 1a3517ed..2cfa476e 100644 --- a/engine/vk/vk_backend.c +++ b/engine/vk/vk_backend.c @@ -1243,6 +1243,7 @@ static const char LIGHTPASS_SHADER[] = "\ {\n\ program rtlight\n\ {\n\ + nodepth\n\ blendfunc add\n\ }\n\ }"; @@ -6355,14 +6356,14 @@ void VKBE_DrawWorld (batch_t **worldbatches) } R_RenderDlights (); + + shaderstate.identitylighting = 1; + BE_RotateForEntity(&r_worldentity, NULL); + if (r_refdef.recurse) RQ_RenderBatch(); else RQ_RenderBatchClear(); - - shaderstate.identitylighting = 1; - - BE_RotateForEntity(&r_worldentity, NULL); } void VKBE_VBO_Begin(vbobctx_t *ctx, size_t maxsize) diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 4c784f31..96f2dec4 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -3259,19 +3259,23 @@ static void VK_PaintScreen(void) scr_con_forcedraw = false; if (noworld) { - extern char levelshotname[]; - //draw the levelshot or the conback fullscreen - if (*levelshotname) - { - shader_t *pic = R2D_SafeCachePic (levelshotname); - int w,h; - if (!R_GetShaderSizes(pic, &w, &h, true)) - w = h = 1; - R2D_Letterbox(0, 0, vid.width, vid.height, pic, w, h); - } + if (R2D_DrawLevelshot()) + ; else if (scr_con_current != vid.height) - R2D_ConsoleBackground(0, vid.height, true); + { +#ifdef HAVE_LEGACY + extern cvar_t dpcompat_console; + if (dpcompat_console.ival) + { + R2D_ImageColours(0,0,0,1); + R2D_FillBlock(0, 0, vid.width, vid.height); + R2D_ImageColours(1,1,1,1); + } + else +#endif + R2D_ConsoleBackground(0, vid.height, true); + } else scr_con_forcedraw = true; diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index de12a1a9..7b9e226f 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -30,9 +30,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define MAX_INFO_KEY 64 +#if defined(SVNREVISION) && defined(SVNDATE) + #define QTVBUILD STRINGIFY(SVNREVISION)", "STRINGIFY(SVNDATE) +#elif defined(SVNREVISION) + #define QTVBUILD STRINGIFY(SVNREVISION)", "__DATE__ +#else + #define QTVBUILD __DATE__ +#endif + //I apologise for this if it breaks your formatting or anything #define HELPSTRING "\ -FTEQTV proxy commands: (build "__DATE__")\n\ +FTEQTV proxy commands: (build "QTVBUILD")\n\ ----------------------\n\ connect, qtv, addserver\n\ connect to a MVD stream (TCP)\n\ diff --git a/imgtool.c b/imgtool.c index 7000ed21..b74d4655 100644 --- a/imgtool.c +++ b/imgtool.c @@ -443,20 +443,21 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna { if (mips->encoding != targfmt) { + qboolean forceformats[PTI_MAX]; for (m = 0; m < PTI_MAX; m++) - sh_config.texfmt[m] = (m == targfmt); - Image_ChangeFormat(mips, args->flags, PTI_INVALID, inname); + forceformats[m] = (m == targfmt); + Image_ChangeFormat(mips, forceformats, PTI_INVALID, inname); if (mips->encoding == targfmt) return true; //switch to common formats... for (m = 0; m < PTI_MAX; m++) - sh_config.texfmt[m] = (m == targfmt) || (m==PTI_RGBA8); - Image_ChangeFormat(mips, args->flags, PTI_INVALID, inname); + forceformats[m] = (m == targfmt) || (m==PTI_RGBA8); + Image_ChangeFormat(mips, forceformats, PTI_INVALID, inname); //and try again... for (m = 0; m < PTI_MAX; m++) - sh_config.texfmt[m] = (m == targfmt); - Image_ChangeFormat(mips, args->flags, PTI_INVALID, inname); + forceformats[m] = (m == targfmt); + Image_ChangeFormat(mips, forceformats, PTI_INVALID, inname); return (mips->encoding == targfmt); } @@ -488,9 +489,10 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna if (!canktx) { + qboolean allowformats[PTI_MAX]; //make sure the source pixel format is acceptable if we're forced to write a png for (m = 0; m < PTI_MAX; m++) - sh_config.texfmt[m] = + allowformats[m] = (m == PTI_RGBA8) || (m == PTI_RGBX8) || (m == PTI_BGRA8) || (m == PTI_BGRX8) || (m == PTI_LLLA8) || (m == PTI_LLLX8) || @@ -499,11 +501,7 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna /*(m == PTI_L16) ||*/ (m == PTI_BGR8) || (m == PTI_BGR8) || 0; - Image_ChangeFormat(mips, args->flags&~IF_PREMULTIPLYALPHA, PTI_INVALID, inname); - - //make sure we properly read whatever is given back to us - for (m = 1; m < countof(sh_config.texfmt); m++) - sh_config.texfmt[m] = true; + Image_ChangeFormat(mips, allowformats, PTI_INVALID, inname); } Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh); @@ -905,9 +903,10 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, else if (!strcmp(outext, ".png")) { #ifdef AVAIL_PNGLIB + qboolean outformats[PTI_MAX]; //force the format, because we can. for (k = 0; k < PTI_MAX; k++) - sh_config.texfmt[k] = + outformats[k] = (k == PTI_RGBA8) || (k == PTI_RGBX8) || (k == PTI_BGRA8) || (k == PTI_BGRX8) || (k == PTI_LLLA8) || (k == PTI_LLLX8) || @@ -918,7 +917,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, 0; if (!sh_config.texfmt[in->encoding]) { - Image_ChangeFormat(in, args->flags&~IF_PREMULTIPLYALPHA, PTI_INVALID, outname); + Image_ChangeFormat(in, outformats, PTI_INVALID, outname); printf("\t(Exporting as %s)\n", Image_FormatName(in->encoding)); } Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh); @@ -930,8 +929,9 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, #ifdef IMAGEFMT_TGA else if (!strcmp(outext, ".tga")) { + qboolean outformats[PTI_MAX]; for (k = 0; k < PTI_MAX; k++) - sh_config.texfmt[k] = + outformats[k] = (k == PTI_RGBA8) || (k == PTI_RGBX8) || (k == PTI_BGRA8) || (k == PTI_BGRX8) || (k == PTI_LLLA8) || (k == PTI_LLLX8) || @@ -940,9 +940,9 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, /*(k == PTI_L16) ||*/ (k == PTI_BGR8) || (k == PTI_BGR8) || 0; - if (!sh_config.texfmt[in->encoding]) + if (!outformats[in->encoding]) { - Image_ChangeFormat(in, args->flags&~IF_PREMULTIPLYALPHA, PTI_INVALID, outname); + Image_ChangeFormat(in, outformats, PTI_INVALID, outname); printf("\t(Exporting as %s)\n", Image_FormatName(in->encoding)); } Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh); @@ -1173,6 +1173,8 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c wad2entry_t *wadentries = NULL, *entry; size_t maxentries = 0; miptex_t mip; + qboolean wadpixelformats[PTI_MAX] = {0}; + wadpixelformats[PTI_P8] = true; ImgTool_TreeScan(&list, srcpath, NULL); f = FS_OpenVFS(destpath, "wb", FS_SYSTEM); @@ -1253,7 +1255,7 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c Con_Printf("%s(%i): WARNING: miptex height is not a not multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[0].width, in->mip[0].height); if (in->encoding != PTI_P8) - Image_ChangeFormat(in, IF_PALETTIZE, (*inname=='{')?TF_TRANS8:PTI_INVALID, inname); + Image_ChangeFormat(in, wadpixelformats, (*inname=='{')?TF_TRANS8:PTI_INVALID, inname); if (in->encoding != PTI_P8) { //erk! we failed to palettize... ImgTool_FreeMips(in); diff --git a/plugins/irc/ircclient.c b/plugins/irc/ircclient.c index 07a5dde4..f708f4a8 100644 --- a/plugins/irc/ircclient.c +++ b/plugins/irc/ircclient.c @@ -66,7 +66,12 @@ static char casevar[9][1000]; //numbered_command #define CURRENTCONSOLE "" // need to make this the current console #define DEFAULTCONSOLE "" #define COMMANDNAME "irc" -#define RELEASE __DATE__ + +#if defined(SVNREVISION) + #define RELEASE STRINGIFY(SVNREVISION) +#else + #define RELEASE __DATE__ +#endif static struct { diff --git a/quakec/menusys/menu/options_video.qc b/quakec/menusys/menu/options_video.qc index 5582e319..43148025 100644 --- a/quakec/menusys/menu/options_video.qc +++ b/quakec/menusys/menu/options_video.qc @@ -1,7 +1,55 @@ +static string() PrepareVideoModes = +{ + string s = _("\"0x0\" \"Default\""); + + for (float i = 0; ; i++) + { + vector m = getresolution(i); + if (!m_x || !m_y) + break; + s = sprintf("%s \"%dx%d\" \"%d x %d\"", s, m_x, m_y, m_x, m_y); + } + return s; +}; + +class options_video : mitem_exmenu +{ + string videomode; + void() options_video = + { + videomode = strcat(super::get("vid_width"), "x", super::get("vid_height")); + }; + + virtual float(string key) isvalid = + { + if (key == "vid_mode") + return super::isvalid("vid_width") && super::isvalid("vid_height"); + return super::isvalid(key); + }; + virtual string(string key) get = + { + if (key == "vid_mode") + return videomode; + return super::get(key); + }; + virtual void(string key, string newval) set = + { + if (key == "vid_mode") + { + videomode = newval; + tokenizebyseparator(newval, "x"); + super::set("vid_width", argv(0)); + super::set("vid_height", argv(1)); + } + else + super::set(key, newval); + }; +}; + nonstatic void(mitem_desktop desktop) M_Options_Video = { mitem_exmenu m; - m = spawn(mitem_exmenu, item_text:_("Video Options"), item_flags:IF_SELECTABLE, item_command:"m_options"); + m = spawn(options_video, item_text:_("Video Options"), item_flags:IF_SELECTABLE, item_command:"m_options"); desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0'); desktop.item_focuschange(m, IF_KFOCUSED); m.totop(); @@ -42,24 +90,7 @@ nonstatic void(mitem_desktop desktop) M_Options_Video = "4 \"4x\" " ), fl, [0, pos], [0, 8]); pos += 8; - //as far as video mode selections go, this is shite. - //should probably have an aspect+modes option instead, but that makes the combo really messy. especially as that would be two cvars. - fr.add(menuitemcombo_spawn(_("Video Width"), "vid_width", '280 8', _( - "0 \"Default\" " - "640 \"640\" " - "800 \"800\" " - "1024 \"1024\" " - "1280 \"1280\" " - "1920 \"1920\" " - )), fl, [0, pos], [0, 8]); pos += 8; - fr.add(menuitemcombo_spawn(_("Video Height"), "vid_height", '280 8', _( - "0 \"Default\" " - "480 \"480\" " - "600 \"600\" " - "768 \"768\" " - "720 \"720\" " - "1080 \"1080\" " - )), fl, [0, pos], [0, 8]); pos += 8; + fr.add(menuitemcombo_spawn(_("Video Mode"), "vid_mode", '280 8', PrepareVideoModes()), fl, [0, pos], [0, 8]); pos += 8; fr.add(menuitemcombo_spawn(_("Video Zoom"), "vid_conautoscale", '280 8', _( "0 \"Default\" " "1.5 \"x1.5\" " @@ -67,8 +98,10 @@ nonstatic void(mitem_desktop desktop) M_Options_Video = "4 \"x4\" " )), fl, [0, pos], [0, 8]); pos += 8; fr.add(menuitemcombo_spawn(_("Colour Depth"), dp("vid_bitsperpixel", "vid_bpp"), '280 8', _( - "16 \"16bit\" " - "32 \"24bit\" " //alpha doesn't count. + "16 \"16bit\" " //r5g6b5, or so. unsupported on win10. + "24 \"24bit\" " //rgba8 - we don't count the alpha. + "30 \"30bit\" " //rgb10a2 - we don't count the alpha. unsupported on nvidia. + "48 \"48bit\" " //half-floats - we don't count the alpha. unsupported on nvidia. )), fl, [0, pos], [0, 8]); pos += 8; fr.add(menuitemcombo_spawn(_("Refresh Rate"), "vid_displayfrequency", '280 8', _( "0 \"Default\" " diff --git a/quakec/menusys/menusys/mitem_desktop.qc b/quakec/menusys/menusys/mitem_desktop.qc index 36032da1..d11c3865 100644 --- a/quakec/menusys/menusys/mitem_desktop.qc +++ b/quakec/menusys/menusys/mitem_desktop.qc @@ -27,7 +27,7 @@ float(string fontname, string fontmaps, string sizes, float slot, optional float void() mitem_desktop::mitem_desktop = { #define menu_font_win autocvar(menu_font_win, "cour") -#define menu_font autocvar(menu_font, "") +#define menu_font autocvar(menu_font, "cour") #define menu_font_fallback autocvar(gl_font, "") queryscreensize(); @@ -40,7 +40,7 @@ void() mitem_desktop::mitem_desktop = fontname = menu_font_win; else if (menu_font != "") fontname = menu_font; - drawfont = loadfont("", fontname, "8 12 16", -1); + drawfont = loadfont("", fontname, "8 12 16 outline=1", -1); } item_text = "desktop"; diff --git a/specs/fte_manifests.txt b/specs/fte_manifests.txt index 7aaed536..f32f2de2 100644 --- a/specs/fte_manifests.txt +++ b/specs/fte_manifests.txt @@ -35,7 +35,7 @@ FTEMANIFEST 1 Specifies the actual version of the manifest file, and what the file actually is. Only valid as the first line. The version number is included for potential future use. Version 1 is the current version. GAME quake -This specifies the code-name of the game upon which your mod is based. +This specifies the code-name of the game upon which your mod is based (and MAY be used to generate homedir paths, so must not contain weird chars). Standalone games should pick their own string to use here. Mods for one of the games that FTE intrinsically understands may be automatically located via the system registry for an existing installation. This is useful when the fmf file has been downloaded and opened via a web browser. Known game name (namely: quake, quake2, quake3, hexen2) may imply other settings if they were not otherwise specified. @@ -97,6 +97,9 @@ MAXVER FTE 4778 The manifest should not be used if the engine parsing it is a higher version than this. See minver for argument info. I'm not sure how useful this will actually be, but it is included for completeness. +MAINCONFIG "fte.cfg" +Isolates fte's config files from those of other engines that might be running this mod. Set to "config.cfg" to annoy users with endless conflicts. + Console Commands: /gamedir foo @@ -117,4 +120,4 @@ Multiple mods specified within a single fmf. Individual files (ie: maps) that can be downloaded on demand? Other stuff. Scan for a new manifest instead of merely switching gamedirs. -Readme/license/credits info is needed. \ No newline at end of file +Readme/license/credits info is needed.