From 6da9f376d7afb831450e655b6049f4997035628f Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sat, 15 Jul 2023 22:10:22 +0200 Subject: [PATCH 01/39] Default `snd_filterquality` to `5` on all platforms for audio consistency (#194) Previously, audio sounded significantly better on Windows than on macOS/Linux due to the default being 5 on Windows and 1 on macOS/Linux. --- Quake/snd_dma.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Quake/snd_dma.c b/Quake/snd_dma.c index 8318c6623..79925599b 100644 --- a/Quake/snd_dma.c +++ b/Quake/snd_dma.c @@ -83,13 +83,7 @@ cvar_t snd_mixspeed = {"snd_mixspeed", "44100", CVAR_NONE}; cvar_t snd_waterfx = {"snd_waterfx", "1", CVAR_ARCHIVE}; -#if defined(_WIN32) -#define SND_FILTERQUALITY_DEFAULT "5" -#else -#define SND_FILTERQUALITY_DEFAULT "1" -#endif - -cvar_t snd_filterquality = {"snd_filterquality", SND_FILTERQUALITY_DEFAULT, +cvar_t snd_filterquality = {"snd_filterquality", "5", CVAR_NONE}; static cvar_t nosound = {"nosound", "0", CVAR_NONE}; @@ -185,7 +179,7 @@ void S_Init (void) Cvar_RegisterVariable(&snd_mixspeed); Cvar_RegisterVariable(&snd_filterquality); Cvar_RegisterVariable(&snd_waterfx); - + if (safemode || COM_CheckParm("-nosound")) return; @@ -1102,4 +1096,3 @@ void S_BeginPrecaching (void) void S_EndPrecaching (void) { } - From 583dfa104eee827e175982469c96ab17ab69f4e9 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 15 Jul 2023 22:20:35 +0200 Subject: [PATCH 02/39] Remove leftover reference to undefined macro --- Quake/snd_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/snd_dma.c b/Quake/snd_dma.c index 79925599b..11534b753 100644 --- a/Quake/snd_dma.c +++ b/Quake/snd_dma.c @@ -122,7 +122,7 @@ static void SND_Callback_snd_filterquality (cvar_t *var) if (snd_filterquality.value < 1 || snd_filterquality.value > 5) { Con_Printf ("snd_filterquality must be between 1 and 5\n"); - Cvar_SetQuick (&snd_filterquality, SND_FILTERQUALITY_DEFAULT); + Cvar_SetQuick (&snd_filterquality, snd_filterquality.default_string); } } From 0b7010e4f63cc3aff324210dced5ebb14a8a5649 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 15 Jul 2023 22:27:34 +0200 Subject: [PATCH 03/39] Save snd_filterquality to config --- Quake/snd_dma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Quake/snd_dma.c b/Quake/snd_dma.c index 11534b753..e9bb7e132 100644 --- a/Quake/snd_dma.c +++ b/Quake/snd_dma.c @@ -83,8 +83,7 @@ cvar_t snd_mixspeed = {"snd_mixspeed", "44100", CVAR_NONE}; cvar_t snd_waterfx = {"snd_waterfx", "1", CVAR_ARCHIVE}; -cvar_t snd_filterquality = {"snd_filterquality", "5", - CVAR_NONE}; +cvar_t snd_filterquality = {"snd_filterquality", "5", CVAR_ARCHIVE}; static cvar_t nosound = {"nosound", "0", CVAR_NONE}; static cvar_t ambient_level = {"ambient_level", "0.3", CVAR_NONE}; From 19c3b2dc2e20221fd58c85be7f77c5ced23ab411 Mon Sep 17 00:00:00 2001 From: OttoHatt <68089630+OttoHatt@users.noreply.github.com> Date: Sat, 15 Jul 2023 21:33:37 +0100 Subject: [PATCH 04/39] Cleanup correct multicurl handle (#233) On Windows with a newer version of Curl (8.1.2), this call to destroy the wrong handle throws an exception. --- Quake/host_cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/host_cmd.c b/Quake/host_cmd.c index 01b01d816..a96ea9e7e 100644 --- a/Quake/host_cmd.c +++ b/Quake/host_cmd.c @@ -642,7 +642,7 @@ static qboolean Download (const char *url, download_t *download) if (header) curl_slist_free_all (header); curl_easy_cleanup (curl); - curl_multi_cleanup (curl); + curl_multi_cleanup (multi_handle); return !download->error && !still_running && download->response == 200; } From c4d66e5cdef9aa3dad32d054c7519d68193d6a57 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 15 Jul 2023 22:37:37 +0200 Subject: [PATCH 05/39] Fix incorrect memset size in SaveData_Init --- Quake/pr_edict.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/pr_edict.c b/Quake/pr_edict.c index 350fc4e71..ed767a624 100644 --- a/Quake/pr_edict.c +++ b/Quake/pr_edict.c @@ -2027,7 +2027,7 @@ int PR_AllocString (int size, char **ptr) void SaveData_Init (savedata_t *save) { - memset (save, 0, sizeof (save)); + memset (save, 0, sizeof (*save)); save->buffersize = 48 * 1024 * 1024; // ad_sepulcher needs ~32 MB save->buffer = (byte *) malloc (save->buffersize); if (!save->buffer) From 694bff9bf1b092b0d43923482c22e332acfee542 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 15 Jul 2023 22:48:40 +0200 Subject: [PATCH 06/39] Enable HTTP 3xx redirection (#205) --- Quake/host_cmd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Quake/host_cmd.c b/Quake/host_cmd.c index a96ea9e7e..959fd3e7f 100644 --- a/Quake/host_cmd.c +++ b/Quake/host_cmd.c @@ -602,6 +602,8 @@ static qboolean Download (const char *url, download_t *download) curl_easy_setopt (curl, CURLOPT_WRITEDATA, download->write_data); curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); curl_easy_setopt (curl, CURLOPT_ACCEPT_ENCODING, ""); + curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 50L); //curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); mc = curl_multi_add_handle (multi_handle, curl); From a3574b537a159bef797949d43bbd5c8fe95108d0 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 15 Jul 2023 23:09:18 +0200 Subject: [PATCH 07/39] Add argument completion for imagelist and imagedump --- Quake/gl_texmgr.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Quake/gl_texmgr.c b/Quake/gl_texmgr.c index 0f272a437..b24887f1e 100644 --- a/Quake/gl_texmgr.c +++ b/Quake/gl_texmgr.c @@ -359,6 +359,20 @@ void TexMgr_ApplySettings (void) Sbar_Changed (); //sbar graphics need to be redrawn with new filter mode } +/* +=============== +TexMgr_Imagelist_Completion_f -- tab completion for imagelist/imagedump +=============== +*/ +static void TexMgr_Imagelist_Completion_f (const char *partial) +{ + gltexture_t *glt; + + for (glt = active_gltextures; glt; glt = glt->next) + if (Con_Match (glt->name, partial)) + Con_AddToTabList (glt->name, partial, NULL); +} + /* =============== TexMgr_Imagelist_f -- report loaded textures @@ -822,6 +836,7 @@ void TexMgr_Init (void) static byte greytexture_data[16] = {127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255}; //50% grey static byte blacktexture_data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //black extern texture_t *r_notexture_mip, *r_notexture_mip2; + cmd_function_t *cmd; // init texture list free_gltextures = (gltexture_t *) Hunk_AllocName (MAX_GLTEXTURES * sizeof(gltexture_t), "gltextures"); @@ -849,8 +864,12 @@ void TexMgr_Init (void) Cvar_RegisterVariable (&r_softemu); Cvar_SetCallback (&r_softemu, TexMgr_SoftEmu_f); Cmd_AddCommand ("gl_describetexturemodes", &TexMgr_DescribeTextureModes_f); - Cmd_AddCommand ("imagelist", &TexMgr_Imagelist_f); - Cmd_AddCommand ("imagedump", &TexMgr_Imagedump_f); + cmd = Cmd_AddCommand ("imagelist", &TexMgr_Imagelist_f); + if (cmd) + cmd->completion = TexMgr_Imagelist_Completion_f; + cmd = Cmd_AddCommand ("imagedump", &TexMgr_Imagedump_f); + if (cmd) + cmd->completion = TexMgr_Imagelist_Completion_f; // poll max size from hardware glGetIntegerv (GL_MAX_TEXTURE_SIZE, &gl_max_texture_size); From d5320c9fab97e2d756ace3f35ab14b965176850b Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 15 Jul 2023 23:39:52 +0200 Subject: [PATCH 08/39] Replace "Screen Size" option with "HUD detail" Inspired by vkQuake --- Quake/menu.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Quake/menu.c b/Quake/menu.c index 53d2a1869..f0e61d0be 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -2933,8 +2933,8 @@ void M_Menu_Video_f (void) def (OPT_PIXELASPECT, "UI Pixels") \ def (OPT_UIMOUSE, "UI Mouse") \ def (OPT_HUDSTYLE, "HUD") \ - def (OPT_SBALPHA, "HUD alpha") \ - def (OPT_SCRSIZE, "Screen Size") \ + def (OPT_SBALPHA, "HUD Alpha") \ + def (OPT_HUDLEVEL, "HUD Detail") \ def (OPT_CROSSHAIR, "Crosshair") \ \ def (OPT_SPACE2, "") \ @@ -3196,10 +3196,10 @@ void M_AdjustSliders (int dir) Cvar_SetValue ("scr_sbarscale", f); Cvar_SetValue ("scr_crosshairscale", f); break; - case OPT_SCRSIZE: // screen size - f = scr_viewsize.value + dir * 10; - if (f > 130) f = 130; - else if(f < 30) f = 30; + case OPT_HUDLEVEL: // hud detail + f = scr_viewsize.value - dir * 10; + if (f > 130) f = 130; + else if(f < 100) f = 100; Cvar_SetValue ("viewsize", f); break; case OPT_PIXELASPECT: // 2D pixel aspect ratio @@ -3426,8 +3426,9 @@ qboolean M_SetSliderValue (int option, float f) M_Options_UpdateLayout (); } return true; - case OPT_SCRSIZE: // screen size - f = f * (130 - 30) + 30; + case OPT_HUDLEVEL: // hud detail + f = 1 - f; + f = f * (130 - 100) + 100; if (f >= 100) f = floor (f / 10 + 0.5) * 10; Cvar_SetValue ("viewsize", f); @@ -3536,8 +3537,9 @@ static void M_Options_DrawItem (int y, int item) M_DrawSlider (x, y, r); break; - case OPT_SCRSIZE: - r = (scr_viewsize.value - 30) / (130 - 30); + case OPT_HUDLEVEL: + r = (scr_viewsize.value - 100) / (130 - 100); + r = 1 - r; M_DrawSlider (x, y, r); break; From 8f055a80fdedb73261eb246340918b43e0bffb78 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 15 Jul 2023 23:47:03 +0200 Subject: [PATCH 09/39] Change default console mode to "insert" (#232) --- Quake/keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/keys.c b/Quake/keys.c index 6a4b67347..f08e4a7c9 100644 --- a/Quake/keys.c +++ b/Quake/keys.c @@ -32,7 +32,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. char key_lines[CMDLINES][MAXCMDLINE]; int key_linepos; -int key_insert; //johnfitz -- insert key toggle (for editing) +int key_insert = 1; //johnfitz -- insert key toggle (for editing) double key_blinktime; //johnfitz -- fudge cursor blinking to make it easier to spot in certain cases int edit_line = 0; From 34ca069d2bf44ece4943456fce3c379d144a8acd Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sun, 16 Jul 2023 00:10:05 +0200 Subject: [PATCH 10/39] Fix viewmodel interpolation with >10Hz animations (#176) --- Quake/progs.h | 2 ++ Quake/r_alias.c | 2 ++ Quake/sv_phys.c | 29 ++++++++++++++--------------- Quake/view.c | 8 ++++++++ 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/Quake/progs.h b/Quake/progs.h index a43a1dc32..9d9c2a176 100644 --- a/Quake/progs.h +++ b/Quake/progs.h @@ -52,6 +52,8 @@ typedef struct edict_s qboolean forcewater; /* mod overrides waterlevel */ qboolean sendforcewater; /* waterlevel override has changed and needs to be sent over to the client */ qboolean sendinterval; /* johnfitz -- send time until nextthink to client for better lerp timing */ + float oldframe; + float oldthinktime; float freetime; /* sv.time when the object was freed */ entvars_t v; /* C exported fields from progs */ diff --git a/Quake/r_alias.c b/Quake/r_alias.c index 4ca6a9505..466ad3c46 100644 --- a/Quake/r_alias.c +++ b/Quake/r_alias.c @@ -136,6 +136,8 @@ void R_SetupAliasFrame (entity_t *e, aliashdr_t *paliashdr, lerpdata_t *lerpdata lerpdata->blend = CLAMP (0.0f, (float)(cl.time - e->lerpstart) / (e->lerpfinish - e->lerpstart), 1.0f); else lerpdata->blend = CLAMP (0.0f, (float)(cl.time - e->lerpstart) / e->lerptime, 1.0f); + if (lerpdata->blend == 1.0f) + e->previouspose = e->currentpose; lerpdata->pose1 = e->previouspose; lerpdata->pose2 = e->currentpose; } diff --git a/Quake/sv_phys.c b/Quake/sv_phys.c index 9f38b6822..ccdbc6df3 100644 --- a/Quake/sv_phys.c +++ b/Quake/sv_phys.c @@ -123,8 +123,6 @@ Returns false if the entity removed itself. qboolean SV_RunThink (edict_t *ent) { float thinktime; - float oldframe; //johnfitz - int i; //johnfitz thinktime = ent->v.nextthink; if (thinktime <= 0 || thinktime > qcvm->time + host_frametime) @@ -135,7 +133,8 @@ qboolean SV_RunThink (edict_t *ent) // it is possible to start that way // by a trigger with a local time. - oldframe = ent->v.frame; //johnfitz + ent->oldthinktime = thinktime; + ent->oldframe = ent->v.frame; //johnfitz ent->v.nextthink = 0; pr_global_struct->time = thinktime; @@ -143,18 +142,6 @@ qboolean SV_RunThink (edict_t *ent) pr_global_struct->other = EDICT_TO_PROG(qcvm->edicts); PR_ExecuteProgram (ent->v.think); -//johnfitz -- PROTOCOL_FITZQUAKE -//capture interval to nextthink here and send it to client for better -//lerp timing, but only if interval is not 0.1 (which client assumes) - ent->sendinterval = false; - if (!ent->free && ent->v.nextthink && (ent->v.movetype == MOVETYPE_STEP || ent->v.frame != oldframe)) - { - i = Q_rint((ent->v.nextthink-thinktime)*255); - if (i >= 0 && i < 256 && i != 25 && i != 26) //25 and 26 are close enough to 0.1 to not send - ent->sendinterval = true; - } -//johnfitz - return !ent->free; } @@ -1235,6 +1222,18 @@ void SV_Physics (void) SV_Physics_Toss (ent); else Sys_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype); + + //johnfitz -- PROTOCOL_FITZQUAKE + //capture interval to nextthink here and send it to client for better + //lerp timing, but only if interval is not 0.1 (which client assumes) + ent->sendinterval = false; + if (!ent->free && ent->v.nextthink > qcvm->time && (ent->v.movetype == MOVETYPE_STEP || ent->v.movetype == MOVETYPE_WALK || ent->v.frame != ent->oldframe)) + { + int j = Q_rint((ent->v.nextthink-ent->oldthinktime)*255); + if (j >= 0 && j < 256 && j != 25 && j != 26) //25 and 26 are close enough to 0.1 to not send + ent->sendinterval = true; + } + //johnfitz } if (pr_global_struct->force_retouch) diff --git a/Quake/view.c b/Quake/view.c index 35977d6d8..538e647c4 100644 --- a/Quake/view.c +++ b/Quake/view.c @@ -837,6 +837,14 @@ void V_CalcRefdef (void) view->origin[2] += 0.5; } + if (ent->lerpflags & LERP_FINISH) + { + view->lerpflags |= LERP_FINISH; + view->lerpfinish = ent->lerpfinish; + } + else + view->lerpflags &= ~LERP_FINISH; + view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; view->frame = cl.stats[STAT_WEAPONFRAME]; view->colormap = vid.colormap; From 8291c51d7bf1f1ab575dc86967f712faa79998b5 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sun, 16 Jul 2023 01:22:04 +0200 Subject: [PATCH 11/39] Remove redundant Con_Match check --- Quake/gl_texmgr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Quake/gl_texmgr.c b/Quake/gl_texmgr.c index b24887f1e..3ca89183e 100644 --- a/Quake/gl_texmgr.c +++ b/Quake/gl_texmgr.c @@ -369,8 +369,7 @@ static void TexMgr_Imagelist_Completion_f (const char *partial) gltexture_t *glt; for (glt = active_gltextures; glt; glt = glt->next) - if (Con_Match (glt->name, partial)) - Con_AddToTabList (glt->name, partial, NULL); + Con_AddToTabList (glt->name, partial, NULL); } /* From d50b1974d7629a51105b954e59020e934c6e0223 Mon Sep 17 00:00:00 2001 From: entryway Date: Sat, 5 Nov 2022 04:57:06 +0200 Subject: [PATCH 12/39] scr_showspeed --- Quake/gl_screen.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Quake/gl_screen.c b/Quake/gl_screen.c index ddf183e14..53a527d52 100644 --- a/Quake/gl_screen.c +++ b/Quake/gl_screen.c @@ -88,6 +88,7 @@ cvar_t scr_conscale = {"scr_conscale", "1", CVAR_ARCHIVE}; cvar_t scr_crosshairscale = {"scr_crosshairscale", "1", CVAR_ARCHIVE}; cvar_t scr_pixelaspect = {"scr_pixelaspect", "1", CVAR_ARCHIVE}; cvar_t scr_showfps = {"scr_showfps", "0", CVAR_ARCHIVE}; +cvar_t scr_showspeed = {"scr_showspeed", "0", CVAR_ARCHIVE}; cvar_t scr_clock = {"scr_clock", "0", CVAR_ARCHIVE}; //johnfitz cvar_t scr_usekfont = {"scr_usekfont", "0", CVAR_NONE}; // 2021 re-release @@ -526,6 +527,7 @@ void SCR_Init (void) Cvar_RegisterVariable (&scr_conscale); Cvar_RegisterVariable (&scr_crosshairscale); Cvar_RegisterVariable (&scr_showfps); + Cvar_RegisterVariable (&scr_showspeed); Cvar_RegisterVariable (&scr_clock); Cvar_RegisterVariable (&scr_hudstyle); Cvar_RegisterVariable (&cl_screenshotname); @@ -632,6 +634,52 @@ void SCR_DrawFPS (void) } } +/* +============== +SCR_DrawSpeed +============== +*/ +void SCR_DrawSpeed (void) +{ + const float show_speed_interval_value = 0.05f; + static float maxspeed = 0, display_speed = -1; + static double lastrealtime = 0; + float speed; + vec3_t vel; + + if (lastrealtime > realtime) + { + lastrealtime = 0; + display_speed = -1; + maxspeed = 0; + } + + VectorCopy (cl.velocity, vel); + vel[2] = 0; + speed = VectorLength (vel); + + if (speed > maxspeed) + maxspeed = speed; + + if (scr_showspeed.value) + { + if (display_speed >= 0) + { + char str[12]; + sprintf (str, "%d", (int) display_speed); + GL_SetCanvas (CANVAS_CROSSHAIR); + Draw_String (-(int)strlen(str)*4, 4, str); + } + } + + if (realtime - lastrealtime >= show_speed_interval_value) + { + lastrealtime = realtime; + display_speed = maxspeed; + maxspeed = 0; + } +} + /* ============== SCR_DrawClock -- johnfitz @@ -1496,6 +1544,7 @@ void SCR_UpdateScreen (void) SCR_DrawConsole (); M_Draw (); SCR_DrawFPS (); //johnfitz + SCR_DrawSpeed (); SCR_DrawSaving (); } From 7af3a3bc6de47546deb464727e25ba5ea9381ef6 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sun, 16 Jul 2023 04:06:11 +0200 Subject: [PATCH 13/39] Draw speed before console/menu --- Quake/gl_screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/gl_screen.c b/Quake/gl_screen.c index 53a527d52..a3e745206 100644 --- a/Quake/gl_screen.c +++ b/Quake/gl_screen.c @@ -1541,10 +1541,10 @@ void SCR_UpdateScreen (void) Sbar_Draw (); SCR_DrawDevStats (); //johnfitz SCR_DrawClock (); //johnfitz + SCR_DrawSpeed (); SCR_DrawConsole (); M_Draw (); SCR_DrawFPS (); //johnfitz - SCR_DrawSpeed (); SCR_DrawSaving (); } From 78f02a77a529709fdad693184a04a8dab180cfdb Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 22 Jul 2023 09:31:51 +0200 Subject: [PATCH 14/39] Tab completion for vid_width/height/refreshrate --- Quake/gl_vidsdl.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index ed406da79..c304a4662 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -1408,6 +1408,59 @@ static void VID_DescribeModes_f (void) // //========================================================================== +/* +================ +VID_CompleteModeField +================ +*/ +static void VID_CompleteModeField (cvar_t *cvar, const char *partial, size_t ofs) +{ + int i; + + #define GET_FIELD_FOR_MODE(idx) *(int*)((uintptr_t)&modelist[idx] + ofs) + + for (i = 0; i < nummodes; i++) + { + char buf[64]; + if (i > 0 && GET_FIELD_FOR_MODE (i) == GET_FIELD_FOR_MODE (i-1)) + continue; + q_snprintf (buf, sizeof (buf), "%d", GET_FIELD_FOR_MODE (i)); + Con_AddToTabList (buf, partial, cvar->value == GET_FIELD_FOR_MODE (i) ? "current" : NULL); + } + + #undef GET_FIELD_FOR_MODE +} + +/* +================ +VID_Width_Completion_f +================ +*/ +static void VID_Width_Completion_f (cvar_t *cvar, const char *partial) +{ + VID_CompleteModeField (cvar, partial, offsetof (vmode_t, width)); +} + +/* +================ +VID_Height_Completion_f +================ +*/ +static void VID_Height_Completion_f (cvar_t *cvar, const char *partial) +{ + VID_CompleteModeField (cvar, partial, offsetof (vmode_t, height)); +} + +/* +================ +VID_Refresh_Completion_f +================ +*/ +static void VID_Refresh_Completion_f (cvar_t *cvar, const char *partial) +{ + VID_CompleteModeField (cvar, partial, offsetof (vmode_t, refreshrate)); +} + /* ================= VID_InitModelist @@ -1487,6 +1540,10 @@ void VID_Init (void) Cvar_SetCallback (&vid_desktopfullscreen, VID_Changed_f); Cvar_SetCallback (&vid_borderless, VID_Changed_f); + Cvar_SetCompletion (&vid_width, VID_Width_Completion_f); + Cvar_SetCompletion (&vid_height, VID_Height_Completion_f); + Cvar_SetCompletion (&vid_refreshrate, VID_Refresh_Completion_f); + Cvar_RegisterVariable (&gl_texture_anisotropy); Cvar_SetCallback (&gl_texture_anisotropy, &TexMgr_Anisotropy_f); From 96272b1e09eea83e483b701eb4472f8000497c15 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 22 Jul 2023 11:35:52 +0200 Subject: [PATCH 15/39] Highlight bottleneck in host_speeds output --- Quake/host.c | 65 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/Quake/host.c b/Quake/host.c index 8d4942a2d..b845969a8 100644 --- a/Quake/host.c +++ b/Quake/host.c @@ -1065,6 +1065,43 @@ static void CL_LoadCSProgs (void) } } +/* +================== +Host_PrintTimes +================== +*/ +static void Host_PrintTimes (const double times[], const char *names[], int count, qboolean showtotal) +{ + char line[1024]; + double total = 0.0; + int i, worst; + + for (i = 0, worst = -1; i < count; i++) + { + if (worst == -1 || times[i] > times[worst]) + worst = i; + total += times[i]; + } + + if (showtotal) + q_snprintf (line, sizeof (line), "%5.2f tot | ", total * 1000.0); + else + line[0] = '\0'; + + for (i = 0; i < count; i++) + { + char entry[256]; + q_snprintf (entry, sizeof (entry), "%5.2f %s", times[i] * 1000.0, names[i]); + if (i == worst) + COM_TintString (entry, entry, sizeof (entry)); + if (i != 0) + q_strlcat (line, " | ", sizeof (line)); + q_strlcat (line, entry, sizeof (line)); + } + + Con_Printf ("%s\n", line); +} + /* ================== Host_Frame @@ -1178,9 +1215,7 @@ void _Host_Frame (double time) if (host_speeds.value) { - static double pass1 = 0.0; - static double pass2 = 0.0; - static double pass3 = 0.0; + static double pass[3] = {0.0, 0.0, 0.0}; static double elapsed = 0.0; static int numframes = 0; static int numserverframes = 0; @@ -1191,26 +1226,24 @@ void _Host_Frame (double time) if (ranserver || host_speeds.value < 0.f) { - pass1 += time1; + pass[0] += time1; numserverframes++; } numframes++; - pass2 += time2; - pass3 += time3; + pass[1] += time2; + pass[2] += time3; elapsed += time; if (elapsed >= host_speeds.value * 0.375) { - pass1 /= q_max (numserverframes, 1); - pass2 /= numframes; - pass3 /= numframes; - if (host_speeds.value < 0.f) - Con_Printf ("%5.2f tot | %5.2f server | %5.2f gfx | %5.2f snd\n", - (pass1+pass2+pass3)*1000.0, pass1*1000.0, pass2*1000.0, pass3*1000.0); - else - Con_Printf ("%5.2f server | %5.2f gfx | %5.2f snd\n", - pass1*1000.0, pass2*1000.0, pass3*1000.0); - pass1 = pass2 = pass3 = elapsed = 0.0; + const char *names[3] = {"server", "gfx", "snd"}; + pass[0] /= q_max (numserverframes, 1); + pass[1] /= numframes; + pass[2] /= numframes; + + Host_PrintTimes (pass, names, countof (pass), host_speeds.value < 0.f); + + pass[0] = pass[1] = pass[2] = elapsed = 0.0; numframes = numserverframes = 0; } } From 7be4aec13dac4c197268d3741b9e6b38da923f00 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Wed, 2 Aug 2023 21:33:43 +0200 Subject: [PATCH 16/39] Add basic tab completion hint display --- Quake/console.c | 51 ++++++++++++++++++++++++++++++++++++++++++------- Quake/console.h | 7 ++++++- Quake/keys.c | 18 ++++++++++++++++- Quake/keys.h | 1 + 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/Quake/console.c b/Quake/console.c index 72a9524c1..0b9044ef5 100644 --- a/Quake/console.c +++ b/Quake/console.c @@ -40,6 +40,7 @@ float con_cursorspeed = 4; #define CON_TEXTSIZE (1024 * 1024) //ericw -- was 65536. johnfitz -- new default size #define CON_MINSIZE 16384 //johnfitz -- old default, now the minimum size +#define CON_MARGIN 1 int con_buffersize; //johnfitz -- user can now override default @@ -116,6 +117,7 @@ void Con_ToggleConsole_f (void) key_linepos = 1; con_backscroll = 0; //johnfitz -- toggleconsole should return you to the bottom of the scrollback history_line = edit_line; //johnfitz -- it should also return you to the bottom of the command history + key_tabhint[0] = '\0'; // clear tab hint if (cls.state == ca_connected) { @@ -262,7 +264,7 @@ void Con_CheckResize (void) char *tbuf; //johnfitz -- tbuf no longer a static array int mark; //johnfitz - width = (vid.conwidth >> 3) - 2; //johnfitz -- use vid.conwidth instead of vid.width + width = (vid.conwidth >> 3) - CON_MARGIN*2; //johnfitz -- use vid.conwidth instead of vid.width if (width == con_linewidth) return; @@ -1080,7 +1082,7 @@ static void BuildTabList (const char *partial) Con_TabComplete -- johnfitz ============ */ -void Con_TabComplete (void) +void Con_TabComplete (tabcomplete_t mode) { char partial[MAXCMDLINE]; const char *match; @@ -1088,6 +1090,16 @@ void Con_TabComplete (void) tab_t *t; int mark, i; + key_tabhint[0] = '\0'; + if (mode == TABCOMPLETE_AUTOHINT) + { + key_tabpartial[0] = '\0'; + + // only show completion hint when the cursor is at the end of the line + if ((size_t)key_linepos >= sizeof (key_lines[edit_line]) || key_lines[edit_line][key_linepos]) + return; + } + // if editline is empty, return if (key_lines[edit_line][1] == 0) return; @@ -1119,8 +1131,8 @@ void Con_TabComplete (void) if (!tablist) return; - // print list if length > 1 - if (tablist->next != tablist) + // print list if length > 1 and action is user-initiated + if (tablist->next != tablist && mode == TABCOMPLETE_USER) { int matches = 0; int total = 0; @@ -1175,6 +1187,17 @@ void Con_TabComplete (void) } while (t != tablist); } + if (mode == TABCOMPLETE_AUTOHINT) + { + size_t len = strlen (partial); + match = q_strcasestr (match, partial); + if (match && match[len]) + q_strlcpy (key_tabhint, match + len, sizeof (key_tabhint)); + Hunk_FreeToLowMark (mark); + key_tabpartial[0] = '\0'; + return; + } + // insert new match into edit line q_strlcpy (partial, match, MAXCMDLINE); //first copy match string q_strlcat (partial, key_lines[edit_line] + key_linepos, MAXCMDLINE); //then add chars after cursor @@ -1200,6 +1223,8 @@ void Con_TabComplete (void) // the changelevel command. the line below "fixes" it, although I'm not sure about // the reason, yet, neither do I know any possible side effects of it: c = key_lines[edit_line] + key_linepos; + + Con_TabComplete (TABCOMPLETE_AUTOHINT); } } @@ -1322,7 +1347,8 @@ extern qpic_t *pic_ovr, *pic_ins; //johnfitz -- new cursor handling void Con_DrawInput (void) { - int i, ofs; + const char *workline = key_lines[edit_line]; + int i, ofs, len; if (key_dest != key_console && !con_forcedup) return; // don't draw anything @@ -1333,9 +1359,20 @@ void Con_DrawInput (void) else ofs = 0; + len = strlen (workline); + // draw input string - for (i = 0; key_lines[edit_line][i+ofs] && i < con_linewidth; i++) - Draw_Character ((i+1)<<3, vid.conheight - 16, key_lines[edit_line][i+ofs]); + for (i = 0; i+ofs < len; i++) + Draw_Character ((i+1)<<3, vid.conheight - 16, workline[i+ofs]); + +// draw tab completion hint + if (key_tabhint[0]) + { + GL_SetCanvasColor (1.0f, 1.0f, 1.0f, 0.75f); + for (i = 0; key_tabhint[i] && i+1+len-ofs < con_linewidth+CON_MARGIN*2; i++) + Draw_Character ((i+1+len-ofs)<<3, vid.conheight - 16, key_tabhint[i] | 0x80); + GL_SetCanvasColor (1.0f, 1.0f, 1.0f, 1.0f); + } // johnfitz -- new cursor handling if (!((int)((realtime-key_blinktime)*con_cursorspeed) & 1)) diff --git a/Quake/console.h b/Quake/console.h index d2f4f71eb..54c4007b6 100644 --- a/Quake/console.h +++ b/Quake/console.h @@ -49,8 +49,13 @@ void Con_ToggleConsole_f (void); void Con_NotifyBox (const char *text); // during startup for sound / cd warnings +typedef enum { + TABCOMPLETE_AUTOHINT, + TABCOMPLETE_USER, +} tabcomplete_t; + const char *Con_Quakebar (int len); -void Con_TabComplete (void); +void Con_TabComplete (tabcomplete_t mode); void Con_AddToTabList (const char *name, const char *partial, const char *type); qboolean Con_Match (const char *str, const char *partial); void Con_LogCenterPrint (const char *str); diff --git a/Quake/keys.c b/Quake/keys.c index f08e4a7c9..a91bf2ed2 100644 --- a/Quake/keys.c +++ b/Quake/keys.c @@ -30,6 +30,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define HISTORY_FILE_NAME "history.txt" char key_lines[CMDLINES][MAXCMDLINE]; +char key_tabhint[MAXCMDLINE]; int key_linepos; int key_insert = 1; //johnfitz -- insert key toggle (for editing) @@ -308,12 +309,13 @@ void Key_Console (int key) key_lines[edit_line][0] = ']'; key_lines[edit_line][1] = 0; //johnfitz -- otherwise old history items show up in the new edit line key_linepos = 1; + key_tabhint[0] = '\0'; if (cls.state == ca_disconnected) SCR_UpdateScreen (); // force an update, because the command may take some time return; case K_TAB: - Con_TabComplete (); + Con_TabComplete (TABCOMPLETE_USER); return; case K_BACKSPACE: @@ -327,6 +329,7 @@ void Key_Console (int key) SDL_assert ((int)len >= numchars); memmove (workline, workline + numchars, len + 1 - numchars); key_linepos -= numchars; + Con_TabComplete (TABCOMPLETE_AUTOHINT); } return; @@ -340,6 +343,7 @@ void Key_Console (int key) len = strlen (workline); SDL_assert ((int)len >= numchars); memmove (workline, workline + numchars, len + 1 - numchars); + Con_TabComplete (TABCOMPLETE_AUTOHINT); } return; @@ -364,12 +368,14 @@ void Key_Console (int key) con_backscroll = CLAMP(0, con_current-i%con_totallines-2, con_totallines-(glheight>>3)-1); } else key_linepos = 1; + Con_TabComplete (TABCOMPLETE_AUTOHINT); return; case K_END: if (keydown[K_CTRL]) con_backscroll = 0; else key_linepos = strlen(workline); + Con_TabComplete (TABCOMPLETE_AUTOHINT); return; case K_PGUP: @@ -394,6 +400,7 @@ void Key_Console (int key) else key_linepos--; key_blinktime = realtime; + Con_TabComplete (TABCOMPLETE_AUTOHINT); } return; @@ -417,6 +424,7 @@ void Key_Console (int key) key_linepos++; key_blinktime = realtime; } + Con_TabComplete (TABCOMPLETE_AUTOHINT); return; case K_UPARROW: @@ -439,6 +447,7 @@ void Key_Console (int key) len = strlen(key_lines[history_line]); memmove(workline, key_lines[history_line], len+1); key_linepos = (int)len; + Con_TabComplete (TABCOMPLETE_AUTOHINT); return; case K_DOWNARROW: @@ -463,12 +472,14 @@ void Key_Console (int key) memmove(workline, key_lines[history_line], len+1); } key_linepos = (int)len; + Con_TabComplete (TABCOMPLETE_AUTOHINT); return; case K_INS: if (keydown[K_SHIFT]) /* Shift-Ins paste */ PasteToConsole(); else key_insert ^= 1; + Con_TabComplete (TABCOMPLETE_AUTOHINT); return; case 'v': @@ -476,11 +487,13 @@ void Key_Console (int key) #if defined(PLATFORM_OSX) || defined(PLATFORM_MAC) if (keydown[K_COMMAND]) { /* Cmd+v paste (Mac-only) */ PasteToConsole(); + Con_TabComplete (TABCOMPLETE_AUTOHINT); return; } #endif if (keydown[K_CTRL]) { /* Ctrl+v paste */ PasteToConsole(); + Con_TabComplete (TABCOMPLETE_AUTOHINT); return; } break; @@ -493,6 +506,7 @@ void Key_Console (int key) workline[1] = 0; key_linepos = 1; history_line= edit_line; + key_tabhint[0] = '\0'; return; } break; @@ -527,6 +541,8 @@ void Char_Console (int key) workline[1] = 0; } key_linepos++; + + Con_TabComplete (TABCOMPLETE_AUTOHINT); } } diff --git a/Quake/keys.h b/Quake/keys.h index 990d22020..bf68d57fc 100644 --- a/Quake/keys.h +++ b/Quake/keys.h @@ -170,6 +170,7 @@ extern char *keybindings[MAX_KEYS]; #define CMDLINES 64 extern char key_lines[CMDLINES][MAXCMDLINE]; +extern char key_tabhint[MAXCMDLINE]; extern int edit_line; extern int key_linepos; extern int key_insert; From 1b936d2a667fe7343fa73598eed12eac89365ce5 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Wed, 2 Aug 2023 22:13:13 +0200 Subject: [PATCH 17/39] Simplify console version printing --- Quake/console.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Quake/console.c b/Quake/console.c index 0b9044ef5..d01fae489 100644 --- a/Quake/console.c +++ b/Quake/console.c @@ -1394,7 +1394,6 @@ void Con_DrawConsole (int lines, qboolean drawinput) { int i, x, y, j, sb, rows; const char *text; - char ver[32]; if (lines <= 0) return; @@ -1437,9 +1436,8 @@ void Con_DrawConsole (int lines, qboolean drawinput) //draw version number in bottom right y += 8; - q_snprintf (ver, sizeof(ver), CONSOLE_TITLE_STRING); - for (x = 0; x < (int)strlen(ver); x++) - Draw_Character ((con_linewidth - strlen(ver) + x + 2)<<3, y, ver[x] /*+ 128*/); + text = CONSOLE_TITLE_STRING; + M_PrintWhite (vid.conwidth - (strlen (text) << 3), y, text); } From 9ff40cacdfeee40522cb95a3c85f54c6c9e5301f Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Wed, 2 Aug 2023 23:28:09 +0200 Subject: [PATCH 18/39] Limit cvar tab completion to a single argument --- Quake/console.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Quake/console.c b/Quake/console.c index d01fae489..c4be42b13 100644 --- a/Quake/console.c +++ b/Quake/console.c @@ -1032,9 +1032,11 @@ static void BuildTabList (const char *partial) if (Cmd_Argc () >= 2) { cvar = Cvar_FindVar (Cmd_Argv (0)); - if (cvar && cvar->completion) + if (cvar) { - cvar->completion (cvar, partial); + // cvars can only have one argument + if (Cmd_Argc () == 2 && cvar->completion) + cvar->completion (cvar, partial); return; } From abfb9f850e0122bfd58a88e8597635dd7759c253 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Wed, 2 Aug 2023 23:53:39 +0200 Subject: [PATCH 19/39] Add model interpolation video option --- Quake/menu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Quake/menu.c b/Quake/menu.c index f0e61d0be..501171e7e 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -53,6 +53,8 @@ extern cvar_t r_softemu; extern cvar_t r_waterwarp; extern cvar_t r_oit; extern cvar_t r_alphasort; +extern cvar_t r_lerpmodels; +extern cvar_t r_lerpmove; extern char crosshair_char; @@ -2969,6 +2971,7 @@ void M_Menu_Video_f (void) def (VID_OPT_SCALE, "Render Scale") \ def (VID_OPT_ANISO, "Anisotropic") \ def (VID_OPT_TEXFILTER, "Textures") \ + def (VID_OPT_ANIMLERP, "Animations") \ def (VID_OPT_PARTICLES, "Particles") \ def (VID_OPT_ALPHAMODE, "Transparency") \ def (VID_OPT_WATERWARP, "Underwater FX") \ @@ -3356,6 +3359,10 @@ void M_AdjustSliders (int dir) case VID_OPT_TEXFILTER: VID_Menu_ChooseNextTexFilter (); break; + case VID_OPT_ANIMLERP: + Cvar_SetValueQuick (&r_lerpmodels, !r_lerpmove.value); + Cvar_SetValueQuick (&r_lerpmove, !r_lerpmove.value); + break; case VID_OPT_PARTICLES: Cvar_SetValueQuick (&r_particles, (int)(q_max (r_particles.value, 0.f) + 3 + dir) % 3); break; @@ -3687,6 +3694,9 @@ static void M_Options_DrawItem (int y, int item) case VID_OPT_TEXFILTER: M_Print (x, y, VID_Menu_GetTexFilterDesc ()); break; + case VID_OPT_ANIMLERP: + M_Print (x, y, r_lerpmodels.value ? "Smooth" : "Classic"); + break; case VID_OPT_PARTICLES: M_Print (x, y, VID_Menu_GetParticlesDesc ()); break; From bac65a51af5b7618f3843d5c6385372242a96e25 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Thu, 3 Aug 2023 00:45:21 +0200 Subject: [PATCH 20/39] Remove mouse/keyboard look options from controls menu --- Quake/menu.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Quake/menu.c b/Quake/menu.c index 501171e7e..23a638149 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -3923,8 +3923,6 @@ static const char* const bindnames[][2] = {"+lookup", "Look up"}, {"+lookdown", "Look down"}, {"centerview", "Center view"}, - {"+mlook", "Mouse look"}, - {"+klook", "Keyboard look"}, {"zoom_in", "Toggle zoom"}, {"+zoom", "Quick zoom"}, {"", ""}, From f75e5ff8bc869b3bc2306b285a8b456b43258b04 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Thu, 3 Aug 2023 01:21:42 +0200 Subject: [PATCH 21/39] Remove leftover macros --- Quake/quakedef.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Quake/quakedef.h b/Quake/quakedef.h index b208d2b73..883476bd9 100644 --- a/Quake/quakedef.h +++ b/Quake/quakedef.h @@ -72,11 +72,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef RC_INVOKED // skip the rest of the file when compiling resources #include "q_stdinc.h" -// !!! if this is changed, it must be changed in d_ifacea.h too !!! -#define CACHE_SIZE 32 // used to align key data structures - -#define Q_UNUSED(x) (x = x) // for pesky compiler / lint warnings - #define MINIMUM_MEMORY 0x550000 #define MINIMUM_MEMORY_LEVELPAK (MINIMUM_MEMORY + 0x100000) From 232c177905ecdba7ad8f5a4b46e11e1ec9e2fec8 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Thu, 3 Aug 2023 14:15:04 +0200 Subject: [PATCH 22/39] Fix Dutch angle VP_PARALLEL_UPRIGHT sprites (#218) --- Quake/r_sprite.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Quake/r_sprite.c b/Quake/r_sprite.c index 849a7f9c4..dbae49d52 100644 --- a/Quake/r_sprite.c +++ b/Quake/r_sprite.c @@ -191,8 +191,10 @@ static void R_DrawSpriteModel_Real (entity_t *e, qboolean showtris) v_up[0] = 0; v_up[1] = 0; v_up[2] = 1; + CrossProduct(v_up, vpn, v_right); + VectorNormalizeFast(v_right); s_up = v_up; - s_right = vright; + s_right = v_right; break; case SPR_FACING_UPRIGHT: //faces camera origin, up is towards the heavens VectorSubtract(e->origin, r_origin, v_forward); From ae359ea7c92e20d02b5f082eb304801b0988dfd0 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Thu, 3 Aug 2023 15:03:20 +0200 Subject: [PATCH 23/39] Fix VP_PARALLEL_UPRIGHT sprite mirroring introduced in 232c177905ecdba7ad8f5a4b46e11e1ec9e2fec8 --- Quake/r_sprite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/r_sprite.c b/Quake/r_sprite.c index dbae49d52..90ced637a 100644 --- a/Quake/r_sprite.c +++ b/Quake/r_sprite.c @@ -191,7 +191,7 @@ static void R_DrawSpriteModel_Real (entity_t *e, qboolean showtris) v_up[0] = 0; v_up[1] = 0; v_up[2] = 1; - CrossProduct(v_up, vpn, v_right); + CrossProduct(vpn, v_up, v_right); VectorNormalizeFast(v_right); s_up = v_up; s_right = v_right; From d8f12b4f321e2ac8a6c1e45620b19f98b16b9600 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Mon, 14 Aug 2023 00:41:25 +0200 Subject: [PATCH 24/39] Add clickable links in console for screenshots, demos, save files, configs, and condumps. --- Quake/cl_demo.c | 7 +- Quake/console.c | 305 ++++++++++++++++++++++++++++++++++++++++++- Quake/console.h | 4 + Quake/gl_screen.c | 6 +- Quake/gl_sky.c | 4 +- Quake/gl_texmgr.c | 9 +- Quake/gl_vidsdl.c | 41 ++++++ Quake/host.c | 8 +- Quake/host_cmd.c | 22 ++-- Quake/in_sdl.c | 6 +- Quake/keys.c | 3 + Quake/sys.h | 3 + Quake/sys_sdl_unix.c | 75 +++++++++++ Quake/sys_sdl_win.c | 51 ++++++++ Quake/vid.h | 7 + 15 files changed, 529 insertions(+), 22 deletions(-) diff --git a/Quake/cl_demo.c b/Quake/cl_demo.c index e05af7d65..ed4120f85 100644 --- a/Quake/cl_demo.c +++ b/Quake/cl_demo.c @@ -301,9 +301,12 @@ void CL_Record_f (void) // open the demo file COM_AddExtension (relname, ".dem", sizeof(relname)); - Con_Printf ("recording to %s.\n", relname); - q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, relname); + + Con_SafePrintf ("Recording to "); + Con_LinkPrintf (name, "%s", relname); + Con_SafePrintf (".\n"); + cls.demofile = Sys_fopen (name, "wb"); if (!cls.demofile) { diff --git a/Quake/console.c b/Quake/console.c index c4be42b13..4783adf8c 100644 --- a/Quake/console.c +++ b/Quake/console.c @@ -52,6 +52,22 @@ int con_current; // where next message will be printed int con_x; // offset in current line for next print char *con_text = NULL; +typedef struct +{ + int line; + int col; +} conofs_t; + +typedef struct +{ + const char *path; + conofs_t begin; + conofs_t end; +} conlink_t; + +static conlink_t **con_links = NULL; +static conlink_t *con_hotlink = NULL; + cvar_t con_notifytime = {"con_notifytime","3",CVAR_NONE}; //seconds cvar_t con_logcenterprint = {"con_logcenterprint", "1", CVAR_NONE}; //johnfitz cvar_t con_notifycenter = {"con_notifycenter", "0", CVAR_ARCHIVE}; @@ -71,6 +87,200 @@ qboolean con_debuglog = false; qboolean con_initialized; +/* +================ +Con_ScreenToOffset + +Converts screen (pixel) coordinates to a console offset +Returns true if the offset is inside the visible portion of the console +================ +*/ +static qboolean Con_ScreenToOffset (int x, int y, conofs_t *ofs) +{ + drawtransform_t transform; + float px, py; + qboolean ret = true; + +// screen space to [-1..1] + px = (x - glx) * 2.f / (float) glwidth - 1.f; + py = (y - gly) * 2.f / (float) glheight - 1.f; + py = -py; + +// [-1..1] to console canvas + Draw_GetCanvasTransform (CANVAS_CONSOLE, &transform); + px = (px - transform.offset[0]) / transform.scale[0]; + py = (py - transform.offset[1]) / transform.scale[1]; + x = (int) (px + 0.5f); + y = (int) (py + 0.5f); + + y = vid.conheight - y; + +// pixels to characters + x >>= 3; + y >>= 3; + +// apply margins and scrolling + x -= CON_MARGIN; + y -= 2; + if (x < 0 || x >= con_linewidth) + ret = false; + if (y < 0 || y >= con_vislines) + ret = false; + if (con_backscroll && y < 2) + ret = false; + + y += con_backscroll; + y = con_current - y; + + ofs->line = y; + ofs->col = x; + + return ret; +} + +/* +================ +Con_OfsCompare + +Performs a three-way comparison on console offsets +================ +*/ +static int Con_OfsCompare (const conofs_t *lhs, const conofs_t *rhs) +{ + if (lhs->line != rhs->line) + return lhs->line - rhs->line; + return lhs->col - rhs->col; +} + +/* +================ +Con_OfsInRange + +Checks if an offset is within a half-open range +================ +*/ +static qboolean Con_OfsInRange (const conofs_t *ofs, const conofs_t *begin, const conofs_t *end) +{ + return Con_OfsCompare (ofs, begin) >= 0 && Con_OfsCompare (ofs, end) < 0; +} + +/* +================ +Con_GetLinkAtOfs + +Returns the link at the given offset, if any, or NULL otherwise +================ +*/ +static conlink_t *Con_GetLinkAtOfs (const conofs_t *ofs) +{ + size_t lo, hi; + +// find the first link that ends after the offset + lo = 0; + hi = VEC_SIZE (con_links); + while (lo < hi) + { + size_t mid = (lo + hi) / 2; + if (Con_OfsCompare (ofs, &con_links[mid]->end) >= 0) + lo = mid + 1; + else + hi = mid; + } + + if (lo == VEC_SIZE (con_links)) + return NULL; + + if (Con_OfsCompare (ofs, &con_links[lo]->begin) >= 0) + return con_links[lo]; + + return NULL; +} + +/* +================ +Con_GetLinkAtPixel + +Returns the link at the given pixel coordinates, if any, or NULL otherwise +================ +*/ +static conlink_t *Con_GetLinkAtPixel (int x, int y) +{ + conofs_t ofs; + if (!Con_ScreenToOffset (x, y, &ofs)) + return NULL; + return Con_GetLinkAtOfs (&ofs); +} + +/* +================ +Con_SetHotLink + +Changes the current hot link and updates the mouse cursor +================ +*/ +static void Con_SetHotLink (conlink_t *link) +{ + if (link == con_hotlink) + return; + con_hotlink = link; + VID_SetMouseCursor (con_hotlink ? MOUSECURSOR_HAND : MOUSECURSOR_DEFAULT); +} + +/* +================ +Con_GetMousePos + +Computes the console offset corresponding to the current mouse position +Returns true if the offset is inside the visible portion of the console +================ +*/ +static qboolean Con_GetMousePos (conofs_t *ofs) +{ + int x, y; + SDL_GetMouseState (&x, &y); + return Con_ScreenToOffset (x, y, ofs); +} + +/* +================ +Con_GetMouseLink + +Returns the link at the current mouse position, if any, or NULL otherwise +================ +*/ +static conlink_t *Con_GetMouseLink (void) +{ + conofs_t ofs; + if (Con_GetMousePos (&ofs)) + return Con_GetLinkAtOfs (&ofs); + return NULL; +} + +/* +================ +Con_Mousemove +Mouse movement callback +================ +*/ +void Con_Mousemove (int x, int y) +{ + Con_SetHotLink (Con_GetLinkAtPixel (x, y)); +} + +/* +================ +Con_Click + +Mouse click callback +================ +*/ +void Con_Click (void) +{ + conlink_t *link = Con_GetMouseLink (); + if (link && !Sys_Explore (link->path)) + S_LocalSound ("misc/menu2.wav"); +} + /* ================ Con_Quakebar -- johnfitz -- returns a bar of the desired length, but never wider than the console @@ -118,6 +328,7 @@ void Con_ToggleConsole_f (void) con_backscroll = 0; //johnfitz -- toggleconsole should return you to the bottom of the scrollback history_line = edit_line; //johnfitz -- it should also return you to the bottom of the command history key_tabhint[0] = '\0'; // clear tab hint + Con_SetHotLink (NULL); if (cls.state == ca_connected) { @@ -146,9 +357,17 @@ Con_Clear_f */ static void Con_Clear_f (void) { + size_t i; + if (con_text) Q_memset (con_text, ' ', con_buffersize); //johnfitz -- con_buffersize replaces CON_TEXTSIZE + con_backscroll = 0; //johnfitz -- if console is empty, being scrolled up is confusing + + Con_SetHotLink (NULL); + for (i = 0; i < VEC_SIZE (con_links); i++) + free (con_links[i]); + VEC_CLEAR (con_links); } /* @@ -168,7 +387,6 @@ static void Con_Dump_f (void) q_strlcpy (relname, Cmd_Argc () >= 2 ? Cmd_Argv (1) : "condump.txt", sizeof (relname)); COM_AddExtension (relname, ".txt", sizeof (relname)); q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, relname); - COM_CreatePath (name); f = Sys_fopen (name, "w"); if (!f) { @@ -207,7 +425,9 @@ static void Con_Dump_f (void) } fclose (f); - Con_Printf ("Dumped console text to %s.\n", relname); + Con_SafePrintf ("Dumped console text to "); + Con_LinkPrintf (name, "%s", relname); + Con_SafePrintf (".\n"); } /* @@ -251,6 +471,13 @@ static void Con_MessageMode2_f (void) } +void Con_RecalcOffset (conofs_t *ofs, int oldnumlines) +{ + ofs->col = q_min (ofs->col, con_linewidth); + ofs->line += con_totallines - 1 - oldnumlines; +} + + /* ================ Con_CheckResize @@ -300,6 +527,13 @@ void Con_CheckResize (void) Hunk_FreeToLowMark (mark); //johnfitz + for (i = 0; i < (int) VEC_SIZE (con_links); i++) + { + conlink_t *link = con_links[i]; + Con_RecalcOffset (&link->begin, con_current); + Con_RecalcOffset (&link->end, con_current); + } + Con_ClearNotify (); con_backscroll = 0; @@ -628,6 +862,58 @@ void Con_DPrintf2 (const char *fmt, ...) } +/* +================== +Con_LinkPrintf + +Prints text that opens a link when clicked +================== +*/ +void Con_LinkPrintf (const char *addr, const char *fmt, ...) +{ + conlink_t *link; + size_t len; + va_list argptr; + char msg[MAXPRINTMSG]; + char *text; + + len = strlen (addr); + link = (conlink_t *) malloc (sizeof (conlink_t) + len + 1); + if (!link) + Sys_Error ("Con_LinkPrintf: out of memory on %" SDL_PRIu64 "u bytes", (uint64_t)(sizeof (conlink_t) + len + 1)); + + memcpy (link + 1, addr, len + 1); + link->path = (const char *)(link + 1); + link->begin.line = con_current; + link->begin.col = con_x; + link->end = link->begin; + + va_start (argptr, fmt); + q_vsnprintf (msg, sizeof(msg), fmt, argptr); + va_end (argptr); + + Con_SafePrintf ("\x02%s", msg); + + link->end.line = con_current; + link->end.col = con_x; + VEC_PUSH (con_links, link); + +// Because of wrapping our text might actually start on the next line, so we skip leading spaces + text = con_text + (link->begin.line % con_totallines)*con_linewidth + link->begin.col; + while (Con_OfsCompare (&link->begin, &link->end) < 0) + { + if ((*text & 0x7f) != ' ') + break; + text++; + if (++link->begin.col == con_linewidth) + { + link->begin.col = 0; + link->begin.line++; + } + } +} + + /* ================== Con_SafePrintf @@ -1414,13 +1700,24 @@ void Con_DrawConsole (int lines, qboolean drawinput) for (i = con_current - rows + 1; i <= con_current - sb; i++, y += 8) { + conofs_t ofs; j = i - con_backscroll; if (j < 0) j = 0; text = con_text + (j % con_totallines)*con_linewidth; - + ofs.line = j; for (x = 0; x < con_linewidth; x++) - Draw_Character ( (x + 1)<<3, y, text[x]); + { + char c = text[x]; + ofs.col = x; + if (con_hotlink && Con_OfsInRange (&ofs, &con_hotlink->begin, &con_hotlink->end)) + { + if (keydown[K_MOUSE1]) + c &= 0x7f; + Draw_Character ((x + 1)<<3, y + 2, '_' | (c & 0x80)); + } + Draw_Character ((x + 1)<<3, y, c); + } } // draw scrollback arrows diff --git a/Quake/console.h b/Quake/console.h index 54c4007b6..12d6d9e0b 100644 --- a/Quake/console.h +++ b/Quake/console.h @@ -42,6 +42,7 @@ void Con_DWarning (const char *fmt, ...) FUNC_PRINTF(1,2); //ericw void Con_Warning (const char *fmt, ...) FUNC_PRINTF(1,2); //johnfitz void Con_DPrintf (const char *fmt, ...) FUNC_PRINTF(1,2); void Con_DPrintf2 (const char *fmt, ...) FUNC_PRINTF(1,2); //johnfitz +void Con_LinkPrintf (const char *addr, const char *fmt, ...) FUNC_PRINTF(2,3); void Con_SafePrintf (const char *fmt, ...) FUNC_PRINTF(1,2); void Con_DrawNotify (void); void Con_ClearNotify (void); @@ -60,6 +61,9 @@ void Con_AddToTabList (const char *name, const char *partial, const char *type); qboolean Con_Match (const char *str, const char *partial); void Con_LogCenterPrint (const char *str); +void Con_Mousemove (int x, int y); +void Con_Click (void); + // // debuglog // diff --git a/Quake/gl_screen.c b/Quake/gl_screen.c index a3e745206..ae21bd641 100644 --- a/Quake/gl_screen.c +++ b/Quake/gl_screen.c @@ -1257,7 +1257,11 @@ void SCR_ScreenShot_f (void) UTF8_ToQuake (basename, sizeof (basename), imagename); if (ok) - Con_Printf ("Wrote %s\n", basename); + { + Con_SafePrintf ("Wrote "); + Con_LinkPrintf (va("%s/%s", com_gamedir, imagename), "%s", basename); + Con_SafePrintf ("\n"); + } else Con_Printf ("SCR_ScreenShot_f: Couldn't create %s\n", basename); diff --git a/Quake/gl_sky.c b/Quake/gl_sky.c index 121b11c6d..3aefe05b3 100644 --- a/Quake/gl_sky.c +++ b/Quake/gl_sky.c @@ -281,7 +281,9 @@ static void Sky_SaveWind_f (void) fclose (f); - Con_Printf ("Wrote '%s'.\n", relname); + Con_SafePrintf ("Wrote "); + Con_LinkPrintf (path, "%s", relname); + Con_SafePrintf ("\n"); } /* diff --git a/Quake/gl_texmgr.c b/Quake/gl_texmgr.c index 3ca89183e..472e65826 100644 --- a/Quake/gl_texmgr.c +++ b/Quake/gl_texmgr.c @@ -435,6 +435,7 @@ TexMgr_Imagedump_f -- dump all current textures to TGA files static void TexMgr_Imagedump_f (void) { char tganame[MAX_OSPATH], tempname[MAX_OSPATH], dirname[MAX_OSPATH]; + const char *reldirname = "imagedump"; const char *filter = NULL; int count = 0; gltexture_t *glt; @@ -444,7 +445,7 @@ static void TexMgr_Imagedump_f (void) if (Cmd_Argc () >= 2) filter = Cmd_Argv (1); - q_snprintf(dirname, sizeof(dirname), "%s/imagedump", com_gamedir); + q_snprintf(dirname, sizeof(dirname), "%s/%s", com_gamedir, reldirname); glPixelStorei (GL_PACK_ALIGNMENT, 1);/* for widths that aren't a multiple of 4 */ @@ -488,9 +489,11 @@ static void TexMgr_Imagedump_f (void) } if (filter) - Con_Printf ("dumped %i textures containing '%s' to %s\n", count, filter, dirname); + Con_SafePrintf ("dumped %i textures containing '%s' to ", count, filter); else - Con_Printf ("dumped %i textures to %s\n", count, dirname); + Con_SafePrintf ("dumped %i textures to ", count); + Con_LinkPrintf (va ("%s/", dirname), "%s", reldirname); + Con_SafePrintf (".\n"); } /* diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index c304a4662..fb8289c7e 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -65,6 +65,8 @@ static qboolean vid_initialized = false; static SDL_Window *draw_context; static SDL_GLContext gl_context; +static SDL_Cursor *cursor_arrow; +static SDL_Cursor *cursor_hand; static qboolean vid_locked = false; //johnfitz static qboolean vid_changed = false; @@ -168,6 +170,43 @@ extern cvar_t host_maxfps; extern cvar_t scr_showfps; extern cvar_t scr_pixelaspect; +//========================================================================== +// +// Mouse cursors +// +//========================================================================== + +static void VID_InitMouseCursors (void) +{ + cursor_arrow = SDL_CreateSystemCursor (SDL_SYSTEM_CURSOR_ARROW); + cursor_hand = SDL_CreateSystemCursor (SDL_SYSTEM_CURSOR_HAND); +} + +static void VID_FreeMouseCursors (void) +{ + SDL_FreeCursor (cursor_arrow); + SDL_FreeCursor (cursor_hand); + cursor_arrow = NULL; + cursor_hand = NULL; +} + +void VID_SetMouseCursor (mousecursor_t cursor) +{ + switch (cursor) + { + case MOUSECURSOR_DEFAULT: + SDL_SetCursor (cursor_arrow); + return; + + case MOUSECURSOR_HAND: + SDL_SetCursor (cursor_hand); + return; + + default: + return; + } +} + //========================================================================== // // HARDWARE GAMMA -- johnfitz @@ -1325,6 +1364,7 @@ void VID_Shutdown (void) { if (vid_initialized) { + VID_FreeMouseCursors(); SDL_QuitSubSystem(SDL_INIT_VIDEO); draw_context = NULL; gl_context = NULL; @@ -1589,6 +1629,7 @@ void VID_Init (void) CFG_ReadCvarOverrides(read_vars, num_readvars); VID_InitModelist(); + VID_InitMouseCursors(); width = (int)vid_width.value; height = (int)vid_height.value; diff --git a/Quake/host.c b/Quake/host.c index b845969a8..926c0b4d5 100644 --- a/Quake/host.c +++ b/Quake/host.c @@ -285,7 +285,9 @@ void Host_WriteConfigurationToFile (const char *name) // config.cfg cvars if (host_initialized && !isDedicated && !host_parms->errstate) { - f = Sys_fopen (va("%s/%s", com_gamedir, name), "w"); + char fullname[MAX_OSPATH]; + q_snprintf (fullname, sizeof (fullname), "%s/%s", com_gamedir, name); + f = Sys_fopen (fullname, "w"); if (!f) { Con_Printf ("Couldn't write %s.\n", name); @@ -304,7 +306,9 @@ void Host_WriteConfigurationToFile (const char *name) fclose (f); - Con_Printf ("Wrote %s.\n", name); + Con_SafePrintf ("Wrote "); + Con_LinkPrintf (fullname, "%s", name); + Con_SafePrintf (".\n"); } } diff --git a/Quake/host_cmd.c b/Quake/host_cmd.c index 959fd3e7f..180bd8d3b 100644 --- a/Quake/host_cmd.c +++ b/Quake/host_cmd.c @@ -2277,10 +2277,11 @@ Host_Savegame_f */ static void Host_Savegame_f (void) { - char relname[MAX_OSPATH]; - char name[MAX_OSPATH]; - FILE *f; - int i; + char relname[MAX_OSPATH]; + char name[MAX_OSPATH]; + const char *skipnotify; + FILE *f; + int i; if (cmd_source != src_command) return; @@ -2332,8 +2333,13 @@ static void Host_Savegame_f (void) q_strlcpy (relname, Cmd_Argv(1), sizeof(relname)); COM_AddExtension (relname, ".sav", sizeof(relname)); + q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, relname); + // second argument, if present, indicates whether or not text should be printed to the notification area - Con_Printf ("%sSaving game to %s...\n", Cmd_Argc () < 3 || atof (Cmd_Argv (2)) ? "" : "[skipnotify]", relname); + skipnotify = (Cmd_Argc () < 3 || atof (Cmd_Argv (2))) ? "" : "[skipnotify]"; + Con_SafePrintf ("%sSaving game to ", skipnotify); + Con_LinkPrintf (name, "%s%s", skipnotify, relname); + Con_SafePrintf ("%s...\n", skipnotify); if (!strcmp (relname, sv.lastsave) && Host_IsSaving ()) { @@ -2344,8 +2350,6 @@ static void Host_Savegame_f (void) SDL_UnlockMutex (save_mutex); } - q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, relname); - f = Sys_fopen (name, "w"); if (!f) { @@ -2428,7 +2432,9 @@ static void Host_Loadgame_f (void) return; } - Con_Printf ("Loading game from %s...\n", relname); + Con_SafePrintf ("Loading game from "); + Con_LinkPrintf (name, "%s", relname); + Con_SafePrintf ("...\n"); SCR_BeginLoadingPlaque (); diff --git a/Quake/in_sdl.c b/Quake/in_sdl.c index 5db48b319..d6246cbe5 100644 --- a/Quake/in_sdl.c +++ b/Quake/in_sdl.c @@ -87,6 +87,8 @@ static int SDLCALL IN_FilterMouseEvents (const SDL_Event *event) // case SDL_MOUSEBUTTONUP: if (key_dest == key_menu) M_Mousemove (event->motion.x, event->motion.y); + else if (key_dest == key_console) + Con_Mousemove (event->motion.x, event->motion.y); return 0; } @@ -244,7 +246,7 @@ void IN_Deactivate (qboolean free_cursor) void IN_DeactivateForConsole (void) { - IN_Deactivate(modestate == MS_WINDOWED); + IN_Deactivate(true); } void IN_DeactivateForMenu (void) @@ -937,6 +939,8 @@ void IN_SendKeyEvents (void) } if (key_dest == key_menu) M_Mousemove (event.button.x, event.button.y); + else if (key_dest == key_console) + Con_Mousemove (event.button.x, event.button.y); Key_Event(buttonremap[event.button.button - 1], event.button.state == SDL_PRESSED); break; diff --git a/Quake/keys.c b/Quake/keys.c index a91bf2ed2..f3cd2bcea 100644 --- a/Quake/keys.c +++ b/Quake/keys.c @@ -962,6 +962,7 @@ void Key_Init (void) consolekeys[K_KP_ENTER] = true; consolekeys[K_KP_INS] = true; consolekeys[K_KP_DEL] = true; + consolekeys[K_MOUSE1] = true; #if defined(PLATFORM_OSX) || defined(PLATFORM_MAC) consolekeys[K_COMMAND] = true; #endif @@ -1140,6 +1141,8 @@ void Key_Event (int key, qboolean down) sprintf (cmd, "-%s %i\n", kb+1, key); Cbuf_AddText (cmd); } + if (key_dest == key_console && key == K_MOUSE1 && !down) + Con_Click (); return; } diff --git a/Quake/sys.h b/Quake/sys.h index 8fd971cca..b5f97bb97 100644 --- a/Quake/sys.h +++ b/Quake/sys.h @@ -57,6 +57,9 @@ const char *Sys_GetEGSLauncherData (void); // (to avoid writing in Nightdive Studios/Quake) qboolean Sys_GetAltUserPrefDir (qboolean remastered, char *path, size_t pathsize); +// shows path in file browser +qboolean Sys_Explore (const char *path); + // // file IO // diff --git a/Quake/sys_sdl_unix.c b/Quake/sys_sdl_unix.c index 144735084..cad59d92b 100644 --- a/Quake/sys_sdl_unix.c +++ b/Quake/sys_sdl_unix.c @@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include +#include #ifdef PLATFORM_OSX #include /* dirname() and basename() */ #endif @@ -409,6 +410,80 @@ qboolean Sys_GetAltUserPrefDir (qboolean remastered, char *dst, size_t dstsize) return true; } +static qboolean Sys_Exec (const char *cmd, ...) +{ + pid_t p = fork (); + if (p < 0) // fork failed + return false; + else if (p == 0) // child process + { + va_list argptr; + const char *argv[1024]; + size_t argc; + FILE *dummy; + + argv[0] = (char *)cmd; + argc = 1; + + va_start (argptr, cmd); + for (; argc + 1 < countof (argv); argc++) + { + const char *cur = va_arg (argptr, const char*); + if (!cur) + break; + argv[argc] = cur; + } + va_end (argptr); + argv[argc++] = NULL; + + // Disable stdout/stderr + // Note: using a dummy variable to avoid triggering -Wunused-result + dummy = freopen ("/dev/null", "w", stdout); (void)dummy; + dummy = freopen ("/dev/null", "w", stderr); (void)dummy; + + execvp (cmd, (char **) argv); + + exit (EXIT_FAILURE); + } + else // original process + { + return true; + } +} + +qboolean Sys_Explore (const char *path) +{ + char buf[32768]; + char *s; + + if (Sys_FileType (path) == FS_ENT_NONE) + return false; + + // Try to identify the current desktop so we can open the parent dir in the file manager *and* select the right file in it + s = getenv ("XDG_CURRENT_DESKTOP"); + if (s) + { + char *cur = NULL; + char *desktop = NULL; + q_strlcpy (buf, s, sizeof (buf)); + for (desktop = strtok_r (buf, ":", &cur); desktop; desktop = strtok_r (NULL, ":", &cur)) + { + if (q_strcasecmp (desktop, "gnome") == 0) + return Sys_Exec ("nautilus", "--select", path); + if (q_strcasecmp (desktop, "kde") == 0) + return Sys_Exec ("dolphin", "--select", path); + } + } + + // Fall back to just opening the parent dir without selecting the file + q_strlcpy (buf, path, sizeof (buf)); + s = Q_strrchr (buf, '/'); + if (!s) + return false; + s[1] = '\0'; // terminate after the slash + return SDL_OpenURL (buf) == 0; +} + #ifdef PLATFORM_OSX static char *OSX_StripAppBundle (char *dir) { /* based on the ioquake3 project at icculus.org. */ diff --git a/Quake/sys_sdl_win.c b/Quake/sys_sdl_win.c index ffd436ad5..4d21b3a27 100644 --- a/Quake/sys_sdl_win.c +++ b/Quake/sys_sdl_win.c @@ -508,6 +508,57 @@ qboolean Sys_GetAltUserPrefDir (qboolean remastered, char *path, size_t pathsize return Sys_GetKnownFolder (&FOLDERID_SavedGames, subdir, path, pathsize); } +qboolean Sys_Explore (const char *path) +{ + wchar_t wpath[MAX_PATH]; + LPITEMIDLIST file, folder; + HRESULT hr; + SFGAOF sfgaof; + int i, slash; + qboolean result = false; + + if (Sys_FileType (path) == FS_ENT_NONE) + return false; + + UTF8ToWideString (path, wpath, countof (wpath)); + for (i = 0, slash = -1; wpath[i]; i++) + { + if (wpath[i] == L'/') + wpath[i] = L'\\'; + if (wpath[i] == L'\\') + slash = i; + } + + if (slash == -1) + return false; + + hr = Sys_InitCOM (); + if (FAILED (hr)) + return false; + + wpath[slash] = L'\0'; + hr = SHParseDisplayName (wpath, NULL, &folder, 0, &sfgaof); + if (FAILED (hr)) + goto cleanup_com; + + wpath[slash] = L'\\'; + hr = SHParseDisplayName (wpath, NULL, &file, 0, &sfgaof); + if (FAILED (hr)) + goto cleanup_folder; + + hr = SHOpenFolderAndSelectItems (folder, 1, &file, 0); + if (SUCCEEDED (hr)) + result = true; + + CoTaskMemFree (file); +cleanup_folder: + CoTaskMemFree (folder); +cleanup_com: + CoUninitialize (); + + return result; +} + static char cwd[1024]; static void Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) diff --git a/Quake/vid.h b/Quake/vid.h index e064ceff5..5134d8208 100644 --- a/Quake/vid.h +++ b/Quake/vid.h @@ -85,11 +85,18 @@ void VID_Shutdown (void); // Called at shutdown void VID_SyncCvars (void); void VID_Toggle (void); +typedef enum +{ + MOUSECURSOR_DEFAULT, + MOUSECURSOR_HAND, +} mousecursor_t; + void *VID_GetWindow (void); qboolean VID_HasMouseOrInputFocus (void); qboolean VID_IsMinimized (void); void VID_Lock (void); void VID_SetWindowTitle (const char *title); +void VID_SetMouseCursor (mousecursor_t cursor); void VID_RecalcConsoleSize (void); void VID_RecalcInterfaceSize (void); From aec68e677ab793ba9427f1f353648fd4d41d8de3 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Mon, 14 Aug 2023 01:14:40 +0200 Subject: [PATCH 25/39] Ignore mouse movement event when binding a mouse button Note: mouse movement was already ignored when binding keys, with one exception: clicking a mouse button to bind it to the current action generates mouse down/up messages, which call mouse move handlers manually before actually handling the click (see IN_SendKeyEvents). --- Quake/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/menu.c b/Quake/menu.c index 23a638149..4043b687c 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -6198,7 +6198,7 @@ void M_Mousemove (int x, int y) drawtransform_t transform; float px, py; - if (!ui_mouse.value) + if (bind_grab || !ui_mouse.value) return; Draw_GetCanvasTransform (CANVAS_MENU, &transform); From d2e713a2ee3b8fb309106ece11259471b41f977e Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Tue, 22 Aug 2023 13:43:45 +0200 Subject: [PATCH 26/39] Add `skywind setview` command version that sets the skywind direction to the current player viewing direction, with optional period and distance arguments. --- Quake/gl_sky.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Quake/gl_sky.c b/Quake/gl_sky.c index 3aefe05b3..f26e18789 100644 --- a/Quake/gl_sky.c +++ b/Quake/gl_sky.c @@ -333,6 +333,24 @@ static void Sky_WindCommand_f (void) return; } + if (!q_strcasecmp (Cmd_Argv (1), "setview")) + { + skybox->wind_yaw = cl.viewangles[YAW]; + skybox->wind_pitch = cl.viewangles[PITCH]; + + if (Cmd_Argc () >= 3) + skybox->wind_period = atof (Cmd_Argv (2)); + else if (!skybox->wind_period) + skybox->wind_period = 30.f; + + if (Cmd_Argc () >= 4) + skybox->wind_dist = CLAMP (-2.0, atof (Cmd_Argv (3)), 2.0); + else if (!skybox->wind_dist) + skybox->wind_dist = 1.f; + + return; + } + if (!q_strcasecmp (Cmd_Argv (1), "rotate")) { if (Cmd_Argc () < 3) From ba6cb9164c6ebc560bdb119917f87679e143a407 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Tue, 22 Aug 2023 13:51:27 +0200 Subject: [PATCH 27/39] Allow negative skywind distances --- Quake/gl_sky.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/gl_sky.c b/Quake/gl_sky.c index f26e18789..9baa0b248 100644 --- a/Quake/gl_sky.c +++ b/Quake/gl_sky.c @@ -775,7 +775,7 @@ Sky_IsAnimated */ qboolean Sky_IsAnimated (void) { - return r_skywind.value != 0.f && skybox && skybox->wind_dist > 0.f; + return r_skywind.value != 0.f && skybox && skybox->wind_dist != 0.f; } /* From 47903f330634cec5737b5f459aa0465845254d39 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Thu, 24 Aug 2023 00:34:14 +0200 Subject: [PATCH 28/39] Fix fullbright palette alpha for non-fullbright colors An alpha of 0 caused color bleed in fence textures: `TexMgr_AlphaEdgeFix` dilated the fullbright portion (with alpha set to 0 on the border), but the shader only checks the alpha channel for the base texture, adding the glow texture on top unconditionally. --- Quake/gl_texmgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/gl_texmgr.c b/Quake/gl_texmgr.c index 472e65826..41a318ded 100644 --- a/Quake/gl_texmgr.c +++ b/Quake/gl_texmgr.c @@ -789,7 +789,7 @@ void TexMgr_LoadPalette (void) else { SetColor (&d_8to24table_alphabright[i], src[0], src[1], src[2], 255); - SetColor (&d_8to24table_fbright[i], 0, 0, 0, 0); + SetColor (&d_8to24table_fbright[i], 0, 0, 0, 255); SetColor (&d_8to24table_nobright[i], src[0], src[1], src[2], 255); } } From 8e2d7054fa8c3142bc1c197440c5f3d683925f62 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Thu, 24 Aug 2023 18:31:19 +0200 Subject: [PATCH 29/39] Remove old overflow-prone MDL meshing code This fixes crashes when loading mj4m4/mj4m5 --- Quake/gl_mesh.c | 301 ----------------------------------------------- Quake/gl_model.h | 3 - 2 files changed, 304 deletions(-) diff --git a/Quake/gl_mesh.c b/Quake/gl_mesh.c index a19dffbaa..2b168f9fc 100644 --- a/Quake/gl_mesh.c +++ b/Quake/gl_mesh.c @@ -35,258 +35,6 @@ ALIAS MODEL DISPLAY LIST GENERATION qmodel_t *aliasmodel; aliashdr_t *paliashdr; -int used[8192]; // qboolean - -// the command list holds counts and s/t values that are valid for -// every frame -int commands[8192]; -int numcommands; - -// all frames will have their vertexes rearranged and expanded -// so they are in the order expected by the command list -int vertexorder[8192]; -int numorder; - -int allverts, alltris; - -int stripverts[128]; -int striptris[128]; -int stripcount; - -/* -================ -StripLength -================ -*/ -int StripLength (int starttri, int startv) -{ - int m1, m2; - int j; - mtriangle_t *last, *check; - int k; - - used[starttri] = 2; - - last = &triangles[starttri]; - - stripverts[0] = last->vertindex[(startv)%3]; - stripverts[1] = last->vertindex[(startv+1)%3]; - stripverts[2] = last->vertindex[(startv+2)%3]; - - striptris[0] = starttri; - stripcount = 1; - - m1 = last->vertindex[(startv+2)%3]; - m2 = last->vertindex[(startv+1)%3]; - - // look for a matching triangle -nexttri: - for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) - { - if (check->facesfront != last->facesfront) - continue; - for (k=0 ; k<3 ; k++) - { - if (check->vertindex[k] != m1) - continue; - if (check->vertindex[ (k+1)%3 ] != m2) - continue; - - // this is the next part of the fan - - // if we can't use this triangle, this tristrip is done - if (used[j]) - goto done; - - // the new edge - if (stripcount & 1) - m2 = check->vertindex[ (k+2)%3 ]; - else - m1 = check->vertindex[ (k+2)%3 ]; - - stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ]; - striptris[stripcount] = j; - stripcount++; - - used[j] = 2; - goto nexttri; - } - } -done: - - // clear the temp used flags - for (j=starttri+1 ; jnumtris ; j++) - if (used[j] == 2) - used[j] = 0; - - return stripcount; -} - -/* -=========== -FanLength -=========== -*/ -int FanLength (int starttri, int startv) -{ - int m1, m2; - int j; - mtriangle_t *last, *check; - int k; - - used[starttri] = 2; - - last = &triangles[starttri]; - - stripverts[0] = last->vertindex[(startv)%3]; - stripverts[1] = last->vertindex[(startv+1)%3]; - stripverts[2] = last->vertindex[(startv+2)%3]; - - striptris[0] = starttri; - stripcount = 1; - - m1 = last->vertindex[(startv+0)%3]; - m2 = last->vertindex[(startv+2)%3]; - - - // look for a matching triangle -nexttri: - for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) - { - if (check->facesfront != last->facesfront) - continue; - for (k=0 ; k<3 ; k++) - { - if (check->vertindex[k] != m1) - continue; - if (check->vertindex[ (k+1)%3 ] != m2) - continue; - - // this is the next part of the fan - - // if we can't use this triangle, this tristrip is done - if (used[j]) - goto done; - - // the new edge - m2 = check->vertindex[ (k+2)%3 ]; - - stripverts[stripcount+2] = m2; - striptris[stripcount] = j; - stripcount++; - - used[j] = 2; - goto nexttri; - } - } -done: - - // clear the temp used flags - for (j=starttri+1 ; jnumtris ; j++) - if (used[j] == 2) - used[j] = 0; - - return stripcount; -} - - -/* -================ -BuildTris - -Generate a list of trifans or strips -for the model, which holds for all frames -================ -*/ -void BuildTris (void) -{ - int i, j, k; - int startv; - float s, t; - int len, bestlen, besttype; - int bestverts[1024]; - int besttris[1024]; - int type; - - // - // build tristrips - // - numorder = 0; - numcommands = 0; - memset (used, 0, sizeof(used)); - for (i = 0; i < pheader->numtris; i++) - { - // pick an unused triangle and start the trifan - if (used[i]) - continue; - - bestlen = 0; - besttype = 0; - for (type = 0 ; type < 2 ; type++) -// type = 1; - { - for (startv = 0; startv < 3; startv++) - { - if (type == 1) - len = StripLength (i, startv); - else - len = FanLength (i, startv); - if (len > bestlen) - { - besttype = type; - bestlen = len; - for (j = 0; j < bestlen+2; j++) - bestverts[j] = stripverts[j]; - for (j = 0; j < bestlen; j++) - besttris[j] = striptris[j]; - } - } - } - - // mark the tris on the best strip as used - for (j = 0; j < bestlen; j++) - used[besttris[j]] = 1; - - if (besttype == 1) - commands[numcommands++] = (bestlen+2); - else - commands[numcommands++] = -(bestlen+2); - - for (j = 0; j < bestlen+2; j++) - { - int tmp; - - // emit a vertex into the reorder buffer - k = bestverts[j]; - vertexorder[numorder++] = k; - - // emit s/t coords into the commands stream - s = stverts[k].s; - t = stverts[k].t; - if (!triangles[besttris[0]].facesfront && stverts[k].onseam) - s += pheader->skinwidth / 2; // on back side - s = (s + 0.5) / pheader->skinwidth; - t = (t + 0.5) / pheader->skinheight; - - // *(float *)&commands[numcommands++] = s; - // *(float *)&commands[numcommands++] = t; - // NOTE: 4 == sizeof(int) - // == sizeof(float) - memcpy (&tmp, &s, 4); - commands[numcommands++] = tmp; - memcpy (&tmp, &t, 4); - commands[numcommands++] = tmp; - } - } - - commands[numcommands++] = 0; // end of list marker - - Con_DPrintf2 ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands); - - allverts += numorder; - alltris += pheader->numtris; -} - static void GL_MakeAliasModelDisplayLists_VBO (void); static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr); @@ -297,58 +45,9 @@ GL_MakeAliasModelDisplayLists */ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr) { - int i, j; - int *cmds; - trivertx_t *verts; - float hscale, vscale; //johnfitz -- padded skins - int count; //johnfitz -- precompute texcoords for padded skins - int *loadcmds; //johnfitz - - //johnfitz -- padded skins - hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth); - vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight); - //johnfitz - aliasmodel = m; paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); -//johnfitz -- generate meshes - Con_DPrintf2 ("meshing %s...\n",m->name); - BuildTris (); - - // save the data out - - paliashdr->poseverts = numorder; - - cmds = (int *) Hunk_Alloc (numcommands * 4); - paliashdr->commands = (byte *)cmds - (byte *)paliashdr; - - //johnfitz -- precompute texcoords for padded skins - loadcmds = commands; - while(1) - { - *cmds++ = count = *loadcmds++; - - if (!count) - break; - - if (count < 0) - count = -count; - - do - { - *(float *)cmds++ = hscale * (*(float *)loadcmds++); - *(float *)cmds++ = vscale * (*(float *)loadcmds++); - } while (--count); - } - //johnfitz - - verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts * sizeof(trivertx_t)); - paliashdr->posedata = (byte *)verts - (byte *)paliashdr; - for (i=0 ; inumposes ; i++) - for (j=0 ; j Date: Thu, 24 Aug 2023 18:35:22 +0200 Subject: [PATCH 30/39] Remove unused globals --- Quake/gl_mesh.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Quake/gl_mesh.c b/Quake/gl_mesh.c index 2b168f9fc..ec05ae450 100644 --- a/Quake/gl_mesh.c +++ b/Quake/gl_mesh.c @@ -52,9 +52,6 @@ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr) GL_MakeAliasModelDisplayLists_VBO (); } -unsigned int r_meshindexbuffer = 0; -unsigned int r_meshvertexbuffer = 0; - /* ================ GL_MakeAliasModelDisplayLists_VBO From 5264e83d3bfb9598a5da7c80686982e65270755b Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Thu, 24 Aug 2023 18:57:43 +0200 Subject: [PATCH 31/39] Minor cleanup --- Quake/gl_mesh.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/Quake/gl_mesh.c b/Quake/gl_mesh.c index ec05ae450..0fb8cd578 100644 --- a/Quake/gl_mesh.c +++ b/Quake/gl_mesh.c @@ -32,29 +32,11 @@ ALIAS MODEL DISPLAY LIST GENERATION ================================================================= */ -qmodel_t *aliasmodel; -aliashdr_t *paliashdr; - -static void GL_MakeAliasModelDisplayLists_VBO (void); static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr); /* ================ GL_MakeAliasModelDisplayLists -================ -*/ -void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr) -{ - aliasmodel = m; - paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); - - // ericw - GL_MakeAliasModelDisplayLists_VBO (); -} - -/* -================ -GL_MakeAliasModelDisplayLists_VBO Saves data needed to build the VBO for this model on the hunk. Afterwards this is copied to Mod_Extradata. @@ -62,7 +44,7 @@ is copied to Mod_Extradata. Original code by MH from RMQEngine ================ */ -void GL_MakeAliasModelDisplayLists_VBO (void) +void GL_MakeAliasModelDisplayLists (qmodel_t *aliasmodel, aliashdr_t *paliashdr) { int i, j; int maxverts_vbo; From 26529ef1ac77bf7c7068a025593b0e6578ea64dd Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Thu, 24 Aug 2023 22:41:10 +0200 Subject: [PATCH 32/39] Optimize GL_MakeAliasModelDisplayLists --- Quake/gl_mesh.c | 63 ++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/Quake/gl_mesh.c b/Quake/gl_mesh.c index 0fb8cd578..1b8f5a15d 100644 --- a/Quake/gl_mesh.c +++ b/Quake/gl_mesh.c @@ -47,9 +47,10 @@ Original code by MH from RMQEngine void GL_MakeAliasModelDisplayLists (qmodel_t *aliasmodel, aliashdr_t *paliashdr) { int i, j; - int maxverts_vbo; + int mark; trivertx_t *verts; unsigned short *indexes; + unsigned short *remap; aliasmesh_t *desc; // first, copy the verts onto the hunk @@ -60,59 +61,63 @@ void GL_MakeAliasModelDisplayLists (qmodel_t *aliasmodel, aliashdr_t *paliashdr) verts[i*paliashdr->numverts + j] = poseverts[i][j]; // there can never be more than this number of verts and we just put them all on the hunk - maxverts_vbo = pheader->numtris * 3; - desc = (aliasmesh_t *) Hunk_Alloc (sizeof (aliasmesh_t) * maxverts_vbo); + // (each vertex can be used twice, once with the original UVs and once with the seam adjustment) + desc = (aliasmesh_t *) Hunk_Alloc (sizeof (aliasmesh_t) * pheader->numverts * 2); // there will always be this number of indexes - indexes = (unsigned short *) Hunk_Alloc (sizeof (unsigned short) * maxverts_vbo); + indexes = (unsigned short *) Hunk_Alloc (sizeof (unsigned short) * pheader->numtris * 3); pheader->indexes = (intptr_t) indexes - (intptr_t) pheader; pheader->meshdesc = (intptr_t) desc - (intptr_t) pheader; pheader->numindexes = 0; pheader->numverts_vbo = 0; + mark = Hunk_LowMark (); + + // each pair of elements in the remap array corresponds to one source vertex + // each value is the final index + 1, or 0 if the corresponding vertex hasn't been emitted yet + remap = (unsigned short *) Hunk_Alloc (paliashdr->numverts * 2 * sizeof (remap[0])); + for (i = 0; i < pheader->numtris; i++) { for (j = 0; j < 3; j++) { - int v; - // index into hdr->vertexes unsigned short vertindex = triangles[i].vertindex[j]; - // basic s/t coords - int s = stverts[vertindex].s; - int t = stverts[vertindex].t; + // index into remap table + int v = vertindex * 2; - // check for back side and adjust texcoord s - if (!triangles[i].facesfront && stverts[vertindex].onseam) s += pheader->skinwidth / 2; + // check for back side + if (!triangles[i].facesfront && stverts[vertindex].onseam) + v++; - // see does this vert already exist - for (v = 0; v < pheader->numverts_vbo; v++) + // emit new vertex if it doesn't already exist + if (!remap[v]) { - // it could use the same xyz but have different s and t - if (desc[v].vertindex == vertindex && (int) desc[v].st[0] == s && (int) desc[v].st[1] == t) - { - // exists; emit an index for it - indexes[pheader->numindexes++] = v; - - // no need to check any more - break; - } - } + // basic s/t coords + int s = stverts[vertindex].s; + int t = stverts[vertindex].t; - if (v == pheader->numverts_vbo) - { - // doesn't exist; emit a new vert and index - indexes[pheader->numindexes++] = pheader->numverts_vbo; + // check for back side and adjust texcoord s + if (v & 1) + s += paliashdr->skinwidth / 2; desc[pheader->numverts_vbo].vertindex = vertindex; desc[pheader->numverts_vbo].st[0] = s; - desc[pheader->numverts_vbo++].st[1] = t; + desc[pheader->numverts_vbo].st[1] = t; + + remap[v] = ++pheader->numverts_vbo; } + + // emit index + indexes[pheader->numindexes++] = remap[v] - 1; } } - + + // free temporary data + Hunk_FreeToLowMark (mark); + // upload immediately GLMesh_LoadVertexBuffer (aliasmodel, pheader); } From 02548d4f23434ebc8eb28ced0087e7fb5182202a Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sun, 27 Aug 2023 11:56:55 +0200 Subject: [PATCH 33/39] Use 16-bit int s/t coords in aliasmesh_t This reduces aliasmesh_t size from 12 to 6 bytes --- Quake/gl_model.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/gl_model.h b/Quake/gl_model.h index 6d24dcf31..f403fbc48 100644 --- a/Quake/gl_model.h +++ b/Quake/gl_model.h @@ -280,7 +280,7 @@ Alias models are position independent, so the cache manager can move them. // split out to keep vertex sizes down typedef struct aliasmesh_s { - float st[2]; + short st[2]; unsigned short vertindex; } aliasmesh_t; From 1709a21a10c8b5d315f4e448d390eea6dc0ea67b Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sun, 27 Aug 2023 12:30:45 +0200 Subject: [PATCH 34/39] Simplify alias model UV computation --- Quake/gl_mesh.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Quake/gl_mesh.c b/Quake/gl_mesh.c index 1b8f5a15d..e8b94aa15 100644 --- a/Quake/gl_mesh.c +++ b/Quake/gl_mesh.c @@ -212,15 +212,15 @@ static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr) float hscale, vscale; //johnfitz -- padded skins - hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth); - vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight); + hscale = 1.0f / (float)TexMgr_PadConditional(hdr->skinwidth); + vscale = 1.0f / (float)TexMgr_PadConditional(hdr->skinheight); //johnfitz st = (meshst_t *) (vbodata + m->vbostofs); for (f = 0; f < hdr->numverts_vbo; f++) { - st[f].st[0] = hscale * ((float) desc[f].st[0] + 0.5f) / (float) hdr->skinwidth; - st[f].st[1] = vscale * ((float) desc[f].st[1] + 0.5f) / (float) hdr->skinheight; + st[f].st[0] = hscale * ((float) desc[f].st[0] + 0.5f); + st[f].st[1] = vscale * ((float) desc[f].st[1] + 0.5f); } } From ed1c5a94a3a1ba34c45e54339868f15608dcd351 Mon Sep 17 00:00:00 2001 From: KurtLoeffler Date: Sun, 27 Aug 2023 10:47:03 -0700 Subject: [PATCH 35/39] Add -prefremaster and -preforiginal command line args. (#202) These args are used to set a preference for which game data to uses when both are available in steam, gog, and egs releases. This allows the game to bypass the version selection dialog on startup. --- Quake/common.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Quake/common.c b/Quake/common.c index e6cdb98fc..42b269e8f 100644 --- a/Quake/common.c +++ b/Quake/common.c @@ -2755,7 +2755,14 @@ static void COM_InitBaseDir (void) { quakeflavor_t flavor; if (original[0] && remastered[0]) - flavor = ChooseQuakeFlavor (); + { + if (COM_CheckParm ("-prefremaster")) + flavor = QUAKE_FLAVOR_REMASTERED; + else if (COM_CheckParm ("-preforiginal")) + flavor = QUAKE_FLAVOR_ORIGINAL; + else + flavor = ChooseQuakeFlavor (); + } else flavor = remastered[0] ? QUAKE_FLAVOR_REMASTERED : QUAKE_FLAVOR_ORIGINAL; q_strlcpy (path, flavor == QUAKE_FLAVOR_REMASTERED ? remastered : original, sizeof (path)); From 59240e2a9276228dd85f9bcbc50171d8cb90f078 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 9 Sep 2023 14:23:26 +0200 Subject: [PATCH 36/39] Clean up CRC code a bit - use CRC_Block instead of byte-wise loop - remove CRC_ProcessByte & CRC_Value (no longer used) - make CRC_Init static - make crctable const --- Quake/common.c | 4 +--- Quake/crc.c | 14 ++------------ Quake/crc.h | 3 --- Quake/pr_edict.c | 4 +--- 4 files changed, 4 insertions(+), 21 deletions(-) diff --git a/Quake/common.c b/Quake/common.c index 42b269e8f..46f322277 100644 --- a/Quake/common.c +++ b/Quake/common.c @@ -2148,9 +2148,7 @@ static pack_t *COM_LoadPackFile (const char *packfile) Sys_FileRead (packhandle, (void *)info, header.dirlen); // crc the directory to check for modifications - CRC_Init (&crc); - for (i = 0; i < header.dirlen; i++) - CRC_ProcessByte (&crc, ((byte *)info)[i]); + crc = CRC_Block (info, header.dirlen); if (crc != PAK0_CRC_V106 && crc != PAK0_CRC_V101 && crc != PAK0_CRC_V100) com_modified = true; diff --git a/Quake/crc.c b/Quake/crc.c index 663655bac..3f73a90e5 100644 --- a/Quake/crc.c +++ b/Quake/crc.c @@ -30,7 +30,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define CRC_INIT_VALUE 0xffff #define CRC_XOR_VALUE 0x0000 -static unsigned short crctable[256] = +static const unsigned short crctable[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, @@ -66,21 +66,11 @@ static unsigned short crctable[256] = 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; -void CRC_Init(unsigned short *crcvalue) +static void CRC_Init(unsigned short *crcvalue) { *crcvalue = CRC_INIT_VALUE; } -void CRC_ProcessByte(unsigned short *crcvalue, byte data) -{ - *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; -} - -unsigned short CRC_Value(unsigned short crcvalue) -{ - return crcvalue ^ CRC_XOR_VALUE; -} - //johnfitz -- texture crc unsigned short CRC_Block (const void *start, int count) { diff --git a/Quake/crc.h b/Quake/crc.h index 7e4cc7f46..ab423badd 100644 --- a/Quake/crc.h +++ b/Quake/crc.h @@ -24,9 +24,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /* crc.h */ -void CRC_Init(unsigned short *crcvalue); -void CRC_ProcessByte(unsigned short *crcvalue, byte data); -unsigned short CRC_Value(unsigned short crcvalue); unsigned short CRC_Block (const void *start, int count); //johnfitz -- texture crc #endif /* _QUAKE_CRC_H */ diff --git a/Quake/pr_edict.c b/Quake/pr_edict.c index ed767a624..ddef0599d 100644 --- a/Quake/pr_edict.c +++ b/Quake/pr_edict.c @@ -1698,9 +1698,7 @@ qboolean PR_LoadProgs (const char *filename, qboolean fatal) return false; Con_DPrintf ("Programs occupy %iK.\n", com_filesize/1024); - CRC_Init (&qcvm->crc); - for (i = 0; i < com_filesize; i++) - CRC_ProcessByte (&qcvm->crc, ((byte *)qcvm->progs)[i]); + qcvm->crc = CRC_Block (qcvm->progs, com_filesize); // byte swap the header for (i = 0; i < (int) sizeof(*qcvm->progs) / 4; i++) From 9497d574dc50f70dd8f1a296360370b3c45a5105 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sat, 9 Sep 2023 14:27:08 +0200 Subject: [PATCH 37/39] Skip pak CRC checks when com_modified is already true --- Quake/common.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Quake/common.c b/Quake/common.c index 46f322277..5cd8fae6e 100644 --- a/Quake/common.c +++ b/Quake/common.c @@ -2111,7 +2111,6 @@ static pack_t *COM_LoadPackFile (const char *packfile) pack_t *pack; int packhandle; dpackfile_t info[MAX_FILES_IN_PACK]; - unsigned short crc; if (Sys_FileOpenRead (packfile, &packhandle) == -1) return NULL; @@ -2148,9 +2147,12 @@ static pack_t *COM_LoadPackFile (const char *packfile) Sys_FileRead (packhandle, (void *)info, header.dirlen); // crc the directory to check for modifications - crc = CRC_Block (info, header.dirlen); - if (crc != PAK0_CRC_V106 && crc != PAK0_CRC_V101 && crc != PAK0_CRC_V100) - com_modified = true; + if (!com_modified) + { + unsigned short crc = CRC_Block (info, header.dirlen); + if (crc != PAK0_CRC_V106 && crc != PAK0_CRC_V101 && crc != PAK0_CRC_V100) + com_modified = true; + } // parse the directory for (i = 0; i < numpackfiles; i++) From 764ef9d16b6a806b1e674d90fcf9e51bb81f7be3 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sun, 24 Sep 2023 13:09:20 +0200 Subject: [PATCH 38/39] Fix potential buffer overflow in COM_Parse e.g. `wad` field longer than 1023 characters --- Quake/common.c | 43 ++++++++++++++++++++++++++++++++++--------- Quake/common.h | 7 +++++++ Quake/gl_fog.c | 2 +- Quake/gl_model.c | 2 +- Quake/gl_rmisc.c | 2 +- Quake/gl_sky.c | 2 +- Quake/pr_edict.c | 5 ++++- 7 files changed, 49 insertions(+), 14 deletions(-) diff --git a/Quake/common.c b/Quake/common.c index 5cd8fae6e..2f056f830 100644 --- a/Quake/common.c +++ b/Quake/common.c @@ -1253,12 +1253,16 @@ char *COM_TintString (const char *in, char *out, size_t outsize) /* ============== -COM_Parse +COM_ParseEx Parse a token out of a string + +The mode argument controls how overflow is handled: +- CPE_NOTRUNC: return NULL (abort parsing) +- CPE_ALLOWTRUNC: truncate com_token (ignore the extra characters in this token) ============== */ -const char *COM_Parse (const char *data) +const char *COM_ParseEx (const char *data, cpe_mode mode) { int c; int len; @@ -1310,16 +1314,20 @@ const char *COM_Parse (const char *data) com_token[len] = 0; return data; } - com_token[len] = c; - len++; + if (len < countof (com_token) - 1) + com_token[len++] = c; + else if (mode == CPE_NOTRUNC) + return NULL; } } // parse single characters if (c == '{' || c == '}'|| c == '('|| c == ')' || c == '\'' || c == ':') { - com_token[len] = c; - len++; + if (len < countof (com_token) - 1) + com_token[len++] = c; + else if (mode == CPE_NOTRUNC) + return NULL; com_token[len] = 0; return data+1; } @@ -1327,9 +1335,11 @@ const char *COM_Parse (const char *data) // parse a regular word do { - com_token[len] = c; + if (len < countof (com_token) - 1) + com_token[len++] = c; + else if (mode == CPE_NOTRUNC) + return NULL; data++; - len++; c = *data; /* commented out the check for ':' so that ip:port works */ if (c == '{' || c == '}'|| c == '('|| c == ')' || c == '\''/* || c == ':' */) @@ -1341,6 +1351,21 @@ const char *COM_Parse (const char *data) } +/* +============== +COM_Parse + +Parse a token out of a string + +Return NULL in case of overflow +============== +*/ +const char *COM_Parse (const char *data) +{ + return COM_ParseEx (data, CPE_NOTRUNC); +} + + /* ================ COM_CheckParm @@ -2082,7 +2107,7 @@ const char *COM_ParseFloatNewline(const char *buffer, float *value) const char *COM_ParseStringNewline(const char *buffer) { int i; - for (i = 0; i < 1023; i++) + for (i = 0; i < countof (com_token) - 1; i++) if (!buffer[i] || q_isspace (buffer[i])) break; memcpy (com_token, buffer, i); diff --git a/Quake/common.h b/Quake/common.h index 2f5c563df..12ad57a39 100644 --- a/Quake/common.h +++ b/Quake/common.h @@ -270,7 +270,14 @@ extern int q_vsnprintf(char *str, size_t size, const char *format, va_list args) extern THREAD_LOCAL char com_token[1024]; extern qboolean com_eof; +typedef enum +{ + CPE_NOTRUNC, // return parse error in case of overflow + CPE_ALLOWTRUNC, // truncate com_token in case of overflow +} cpe_mode; + const char *COM_Parse (const char *data); +const char *COM_ParseEx (const char *data, cpe_mode mode); extern int com_argc; diff --git a/Quake/gl_fog.c b/Quake/gl_fog.c index d01b883c4..4173b4282 100644 --- a/Quake/gl_fog.c +++ b/Quake/gl_fog.c @@ -224,7 +224,7 @@ void Fog_ParseWorldspawn (void) q_strlcpy(key, com_token, sizeof(key)); while (key[0] && key[strlen(key)-1] == ' ') // remove trailing spaces key[strlen(key)-1] = 0; - data = COM_Parse(data); + data = COM_ParseEx(data, CPE_ALLOWTRUNC); if (!data) return; // error q_strlcpy(value, com_token, sizeof(value)); diff --git a/Quake/gl_model.c b/Quake/gl_model.c index 075844490..766299b38 100644 --- a/Quake/gl_model.c +++ b/Quake/gl_model.c @@ -2539,7 +2539,7 @@ qboolean Mod_LoadMapDescription (char *desc, size_t maxchars, const char *map) is_classname = i != 0 && !strcmp (com_token, "classname"); // parse value - data = COM_Parse (data); + data = COM_ParseEx (data, CPE_ALLOWTRUNC); if (!data) return ret; diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index 4c3407d6f..dad1b52e4 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -407,7 +407,7 @@ static void R_ParseWorldspawn (void) q_strlcpy(key, com_token, sizeof(key)); while (key[0] && key[strlen(key)-1] == ' ') // remove trailing spaces key[strlen(key)-1] = 0; - data = COM_Parse(data); + data = COM_ParseEx(data, CPE_ALLOWTRUNC); if (!data) return; // error q_strlcpy(value, com_token, sizeof(value)); diff --git a/Quake/gl_sky.c b/Quake/gl_sky.c index 9baa0b248..d69ff7c1f 100644 --- a/Quake/gl_sky.c +++ b/Quake/gl_sky.c @@ -559,7 +559,7 @@ void Sky_NewMap (void) q_strlcpy(key, com_token, sizeof(key)); while (key[0] && key[strlen(key)-1] == ' ') // remove trailing spaces key[strlen(key)-1] = 0; - data = COM_Parse(data); + data = COM_ParseEx(data, CPE_ALLOWTRUNC); if (!data) return; // error q_strlcpy(value, com_token, sizeof(value)); diff --git a/Quake/pr_edict.c b/Quake/pr_edict.c index ddef0599d..5c7c7fae9 100644 --- a/Quake/pr_edict.c +++ b/Quake/pr_edict.c @@ -1144,7 +1144,10 @@ const char *ED_ParseEdict (const char *data, edict_t *ent) } // parse value - data = COM_Parse (data); + // HACK: we allow truncation when reading the wad field, + // otherwise maps using lots of wads with absolute paths + // could cause a parse error + data = COM_ParseEx (data, !strcmp (keyname, "wad") ? CPE_ALLOWTRUNC : CPE_NOTRUNC); if (!data) Host_Error ("ED_ParseEntity: EOF without closing brace"); From 64a8b65f4157bcf8a590ca1e763ffa96b3e8e0b3 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Sun, 24 Sep 2023 13:28:16 +0200 Subject: [PATCH 39/39] Add -remaster/-remastered/-original cmdline options as aliases for -prefremaster and -preforiginal --- Quake/common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Quake/common.c b/Quake/common.c index 2f056f830..d1a9e5553 100644 --- a/Quake/common.c +++ b/Quake/common.c @@ -2781,9 +2781,9 @@ static void COM_InitBaseDir (void) quakeflavor_t flavor; if (original[0] && remastered[0]) { - if (COM_CheckParm ("-prefremaster")) + if (COM_CheckParm ("-prefremaster") || COM_CheckParm ("-remaster") || COM_CheckParm ("-remastered")) flavor = QUAKE_FLAVOR_REMASTERED; - else if (COM_CheckParm ("-preforiginal")) + else if (COM_CheckParm ("-preforiginal") || COM_CheckParm ("-original")) flavor = QUAKE_FLAVOR_ORIGINAL; else flavor = ChooseQuakeFlavor ();