]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_screen.c
only send prydoncursor related buttons, if cl_prydoncursor is 1
[xonotic/darkplaces.git] / cl_screen.c
index e26048ef936afb80791ace7b306ee7e0f37e42ad..9319ca858196aae70394e5a6e15a55704c4f097c 100644 (file)
@@ -3,60 +3,97 @@
 #include "cl_video.h"
 #include "image.h"
 #include "jpeg.h"
+#include "image_png.h"
 #include "cl_collision.h"
+#include "libcurl.h"
 #include "csprogs.h"
+#include "cap_avi.h"
+#include "cap_ogg.h"
+
+// we have to include snd_main.h here only to get access to snd_renderbuffer->format.speed when writing the AVI headers
+#include "snd_main.h"
 
 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100", "how large the view should be, 110 disables inventory bar, 120 disables status bar"};
-cvar_t scr_fov = {CVAR_SAVE, "fov","90", "field of vision, 1-170 degrees, default 90, some players use 110-130"};      // 1 - 170
-cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900", "speed of console open/close"}; // LordHavoc: quake used 300
-cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1", "opacity of console background"};
-cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "0.2", "brightness of console background (0 = black, 1 = image)"};
+cvar_t scr_fov = {CVAR_SAVE, "fov","90", "field of vision, 1-170 degrees, default 90, some players use 110-130"};
+cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1", "opacity of console background gfx/conback"};
+cvar_t scr_conalphafactor = {CVAR_SAVE, "scr_conalphafactor", "1", "opacity of console background gfx/conback relative to scr_conalpha; when 0, gfx/conback is not drawn"};
+cvar_t scr_conalpha2factor = {CVAR_SAVE, "scr_conalpha2factor", "0", "opacity of console background gfx/conback2 relative to scr_conalpha; when 0, gfx/conback2 is not drawn"};
+cvar_t scr_conalpha3factor = {CVAR_SAVE, "scr_conalpha3factor", "0", "opacity of console background gfx/conback3 relative to scr_conalpha; when 0, gfx/conback3 is not drawn"};
+cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "1", "brightness of console background (0 = black, 1 = image)"};
 cvar_t scr_conforcewhiledisconnected = {0, "scr_conforcewhiledisconnected", "1", "forces fullscreen console while disconnected"};
-cvar_t scr_menuforcewhiledisconnected = {0, "scr_menuforcewhiledisconnected", "1", "forces menu while disconnected"};
+cvar_t scr_conscroll_x = {CVAR_SAVE, "scr_conscroll_x", "0", "scroll speed of gfx/conback in x direction"};
+cvar_t scr_conscroll_y = {CVAR_SAVE, "scr_conscroll_y", "0", "scroll speed of gfx/conback in y direction"};
+cvar_t scr_conscroll2_x = {CVAR_SAVE, "scr_conscroll2_x", "0", "scroll speed of gfx/conback2 in x direction"};
+cvar_t scr_conscroll2_y = {CVAR_SAVE, "scr_conscroll2_y", "0", "scroll speed of gfx/conback2 in y direction"};
+cvar_t scr_conscroll3_x = {CVAR_SAVE, "scr_conscroll3_x", "0", "scroll speed of gfx/conback3 in x direction"};
+cvar_t scr_conscroll3_y = {CVAR_SAVE, "scr_conscroll3_y", "0", "scroll speed of gfx/conback3 in y direction"};
+cvar_t scr_menuforcewhiledisconnected = {0, "scr_menuforcewhiledisconnected", "0", "forces menu while disconnected"};
 cvar_t scr_centertime = {0, "scr_centertime","2", "how long centerprint messages show"};
 cvar_t scr_showram = {CVAR_SAVE, "showram","1", "show ram icon if low on surface cache memory (not used)"};
-cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0", "show turtle icon when framerate is too low (not used)"};
+cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0", "show turtle icon when framerate is too low"};
 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1", "show pause icon when game is paused"};
 cvar_t scr_showbrand = {0, "showbrand","0", "shows gfx/brand.tga in a corner of the screen (different values select different positions, including centered)"};
-cvar_t scr_printspeed = {0, "scr_printspeed","8", "speed of intermission printing (episode end texts)"};
+cvar_t scr_printspeed = {0, "scr_printspeed","0", "speed of intermission printing (episode end texts), a value of 0 disables the slow printing"};
+cvar_t scr_loadingscreen_background = {0, "scr_loadingscreen_background","0", "show the last visible background during loading screen (costs one screenful of video memory)"};
+cvar_t scr_loadingscreen_count = {0, "scr_loadingscreen_count","1", "number of loading screen files to use randomly (named loading.tga, loading2.tga, loading3.tga, ...)"};
+cvar_t scr_loadingscreen_barcolor = {0, "scr_loadingscreen_barcolor", "0 0 1", "rgb color of loadingscreen progress bar"};
+cvar_t scr_loadingscreen_barheight = {0, "scr_loadingscreen_barheight", "8", "a height loadingscreen progress bar"};
 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640", "virtual width of 2D graphics system"};
 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480", "virtual height of 2D graphics system"};
 cvar_t vid_pixelheight = {CVAR_SAVE, "vid_pixelheight", "1", "adjusts vertical field of vision to account for non-square pixels (1280x1024 on a CRT monitor for example)"};
 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","1", "save jpeg instead of targa"};
 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9", "image quality of saved jpeg"};
-cvar_t scr_screenshot_gamma = {CVAR_SAVE, "scr_screenshot_gamma","2.2", "gamma correction on saved screenshots and videos, 1.0 saves unmodified images"};
+cvar_t scr_screenshot_png = {CVAR_SAVE, "scr_screenshot_png","0", "save png instead of targa"};
+cvar_t scr_screenshot_gammaboost = {CVAR_SAVE, "scr_screenshot_gammaboost","1", "gamma correction on saved screenshots and videos, 1.0 saves unmodified images"};
+cvar_t scr_screenshot_hwgamma = {CVAR_SAVE, "scr_screenshot_hwgamma","1", "apply the video gamma ramp to saved screenshots and videos"};
+cvar_t scr_screenshot_alpha = {CVAR_SAVE, "scr_screenshot_alpha","0", "try to write an alpha channel to screenshots (debugging feature)"};
 // scr_screenshot_name is defined in fs.c
-cvar_t cl_capturevideo = {0, "cl_capturevideo", "0", "enables saving of video to a file or files (default is .tga files, if scr_screenshot_jpeg is on it saves .jpg files (VERY SLOW), if any rawrgb or rawyv12 are on it saves those formats instead, note that scr_screenshot_gamma affects the brightness of the output)"};
-cvar_t cl_capturevideo_sound = {0, "cl_capturevideo_sound", "0", "enables saving of sound to a .wav file (warning: this requires exact sync, if your hard drive can't keep up it will abort, if your graphics can't keep up it will save duplicate frames to maintain sound sync)"};
-cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30", "how many frames per second to save (29.97 for NTSC, 30 for typical PC video, 15 can be useful)"};
-cvar_t cl_capturevideo_rawrgb = {0, "cl_capturevideo_rawrgb", "0", "saves a single .rgb video file containing raw RGB images (you'll need special processing tools to encode this to something more useful)"};
-cvar_t cl_capturevideo_rawyv12 = {0, "cl_capturevideo_rawyv12", "0", "saves a single .yv12 video file containing raw YV12 (luma plane, then half resolution chroma planes, first chroma blue then chroma red, this is the format used internally by many encoders, some tools can read it directly)"};
+cvar_t cl_capturevideo = {0, "cl_capturevideo", "0", "enables saving of video to a .avi file using uncompressed I420 colorspace and PCM audio, note that scr_screenshot_gammaboost affects the brightness of the output)"};
+cvar_t cl_capturevideo_printfps = {CVAR_SAVE, "cl_capturevideo_printfps", "1", "prints the frames per second captured in capturevideo (is only written to the log file, not to the console, as that would be visible on the video)"};
+cvar_t cl_capturevideo_width = {CVAR_SAVE, "cl_capturevideo_width", "0", "scales all frames to this resolution before saving the video"};
+cvar_t cl_capturevideo_height = {CVAR_SAVE, "cl_capturevideo_height", "0", "scales all frames to this resolution before saving the video"};
+cvar_t cl_capturevideo_realtime = {0, "cl_capturevideo_realtime", "0", "causes video saving to operate in realtime (mostly useful while playing, not while capturing demos), this can produce a much lower quality video due to poor sound/video sync and will abort saving if your machine stalls for over a minute"};
+cvar_t cl_capturevideo_fps = {CVAR_SAVE, "cl_capturevideo_fps", "30", "how many frames per second to save (29.97 for NTSC, 30 for typical PC video, 15 can be useful)"};
+cvar_t cl_capturevideo_nameformat = {CVAR_SAVE, "cl_capturevideo_nameformat", "dpvideo", "prefix for saved videos (the date is encoded using strftime escapes)"};
+cvar_t cl_capturevideo_number = {CVAR_SAVE, "cl_capturevideo_number", "1", "number to append to video filename, incremented each time a capture begins"};
+cvar_t cl_capturevideo_ogg = {CVAR_SAVE, "cl_capturevideo_ogg", "1", "save captured video data as Ogg/Vorbis/Theora streams"};
+cvar_t cl_capturevideo_framestep = {CVAR_SAVE, "cl_capturevideo_framestep", "1", "when set to n >= 1, render n frames to capture one (useful for motion blur like effects)"};
 cvar_t r_letterbox = {0, "r_letterbox", "0", "reduces vertical height of view to simulate a letterboxed movie effect (can be used by mods for cutscenes)"};
-cvar_t r_stereo_separation = {0, "r_stereo_separation", "4", "separation of eyes in the world (try negative values too)"};
-cvar_t r_stereo_sidebyside = {0, "r_stereo_sidebyside", "0", "side by side views (for those who can't afford glasses but can afford eye strain)"};
+cvar_t r_stereo_separation = {0, "r_stereo_separation", "4", "separation distance of eyes in the world (negative values are only useful for cross-eyed viewing)"};
+cvar_t r_stereo_sidebyside = {0, "r_stereo_sidebyside", "0", "side by side views for those who can't afford glasses but can afford eye strain (note: use a negative r_stereo_separation if you want cross-eyed viewing)"};
 cvar_t r_stereo_redblue = {0, "r_stereo_redblue", "0", "red/blue anaglyph stereo glasses (note: most of these glasses are actually red/cyan, try that one too)"};
 cvar_t r_stereo_redcyan = {0, "r_stereo_redcyan", "0", "red/cyan anaglyph stereo glasses, the kind given away at drive-in movies like Creature From The Black Lagoon In 3D"};
 cvar_t r_stereo_redgreen = {0, "r_stereo_redgreen", "0", "red/green anaglyph stereo glasses (for those who don't mind yellow)"};
+cvar_t r_stereo_angle = {0, "r_stereo_angle", "0", "separation angle of eyes (makes the views look different directions, as an example, 90 gives a 90 degree separation where the views are 45 degrees left and 45 degrees right)"};
 cvar_t scr_zoomwindow = {CVAR_SAVE, "scr_zoomwindow", "0", "displays a zoomed in overlay window"};
 cvar_t scr_zoomwindow_viewsizex = {CVAR_SAVE, "scr_zoomwindow_viewsizex", "20", "horizontal viewsize of zoom window"};
 cvar_t scr_zoomwindow_viewsizey = {CVAR_SAVE, "scr_zoomwindow_viewsizey", "20", "vertical viewsize of zoom window"};
 cvar_t scr_zoomwindow_fov = {CVAR_SAVE, "scr_zoomwindow_fov", "20", "fov of zoom window"};
+cvar_t scr_stipple = {0, "scr_stipple", "0", "interlacing-like stippling of the display"};
+cvar_t scr_refresh = {0, "scr_refresh", "1", "allows you to completely shut off rendering for benchmarking purposes"};
+cvar_t scr_screenshot_name_in_mapdir = {CVAR_SAVE, "scr_screenshot_name_in_mapdir", "0", "if set to 1, screenshots are placed in a subdirectory named like the map they are from"};
+cvar_t shownetgraph = {CVAR_SAVE, "shownetgraph", "0", "shows a graph of packet sizes and other information, 0 = off, 1 = show client netgraph, 2 = show client and server netgraphs (when hosting a server)"};
+cvar_t cl_demo_mousegrab = {0, "cl_demo_mousegrab", "0", "Allows reading the mouse input while playing demos. Useful for camera mods developed in csqc. (0: never, 1: always)"};
+cvar_t timedemo_screenshotframelist = {0, "timedemo_screenshotframelist", "", "when performing a timedemo, take screenshots of each frame in this space-separated list - example: 1 201 401"};
 
+extern cvar_t v_glslgamma;
+extern cvar_t sbar_info_pos;
+#define WANT_SCREENSHOT_HWGAMMA (scr_screenshot_hwgamma.integer && vid_usinghwgamma)
 
 int jpeg_supported = false;
 
 qboolean       scr_initialized;                // ready to draw
 
 float          scr_con_current;
+int                    scr_con_margin_bottom;
 
 extern int     con_vislines;
 
-void DrawCrosshair(int num);
 static void SCR_ScreenShot_f (void);
 static void R_Envmap_f (void);
 
 // backend
-void R_ClearScreen(void);
+void R_ClearScreen(qboolean fogcolor);
 
 /*
 ===============================================================================
@@ -72,6 +109,8 @@ float                scr_centertime_off;
 int                    scr_center_lines;
 int                    scr_erase_lines;
 int                    scr_erase_center;
+char        scr_infobarstring[MAX_INPUTLINE];
+float       scr_infobartime_off;
 
 /*
 ==============
@@ -81,7 +120,7 @@ Called for important messages that should stay in the center of the screen
 for a few moments
 ==============
 */
-void SCR_CenterPrint(char *str)
+void SCR_CenterPrint(const char *str)
 {
        strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
        scr_centertime_off = scr_centertime.value;
@@ -101,14 +140,20 @@ void SCR_CenterPrint(char *str)
 void SCR_DrawCenterString (void)
 {
        char    *start;
-       int             l;
        int             x, y;
        int             remaining;
        int             color;
 
-// the finale prints the characters one at a time
-       if (cl.intermission)
-               remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
+       if(cl.intermission == 2) // in finale,
+               if(sb_showscores) // make TAB hide the finale message (sb_showscores overrides finale in sbar.c)
+                       return;
+
+       if(scr_centertime.value <= 0 && !cl.intermission)
+               return;
+
+// the finale prints the characters one at a time, except if printspeed is an absurdly high value
+       if (cl.intermission && scr_printspeed.value > 0 && scr_printspeed.value < 1000000)
+               remaining = (int)(scr_printspeed.value * (cl.time - scr_centertime_start));
        else
                remaining = 9999;
 
@@ -119,36 +164,33 @@ void SCR_DrawCenterString (void)
                return;
 
        if (scr_center_lines <= 4)
-               y = vid_conheight.integer*0.35;
+               y = (int)(vid_conheight.integer*0.35);
        else
                y = 48;
 
        color = -1;
        do
        {
-       // scan the width of the line
-               for (l=0 ; l<vid_conwidth.integer/8 ; l++)
-                       if (start[l] == '\n' || !start[l])
-                               break;
-               x = (vid_conwidth.integer - l*8)/2;
+               // scan the number of characters on the line, not counting color codes
+               char *newline = strchr(start, '\n');
+               int l = newline ? (newline - start) : (int)strlen(start);
+               float width = DrawQ_TextWidth(start, l, 8, 8, false, FONT_CENTERPRINT);
+
+               x = (int) (vid_conwidth.integer - width)/2;
                if (l > 0)
                {
                        if (remaining < l)
                                l = remaining;
-                       DrawQ_ColoredString(x, y, start, l, 8, 8, 1, 1, 1, 1, 0, &color);
+                       DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0, &color, false, FONT_CENTERPRINT);
                        remaining -= l;
                        if (remaining <= 0)
                                return;
                }
-
                y += 8;
 
-               while (*start && *start != '\n')
-                       start++;
-
-               if (!*start)
+               if (!newline)
                        break;
-               start++;                // skip the \n
+               start = newline + 1; // skip the \n
        } while (1);
 }
 
@@ -157,7 +199,8 @@ void SCR_CheckDrawCenterString (void)
        if (scr_center_lines > scr_erase_lines)
                scr_erase_lines = scr_center_lines;
 
-       scr_centertime_off -= host_frametime;
+       if (cl.time > cl.oldtime)
+               scr_centertime_off -= cl.time - cl.oldtime;
 
        // don't draw if this is a normal stats-screen intermission,
        // only if it is not an intermission, or a finale intermission
@@ -171,6 +214,121 @@ void SCR_CheckDrawCenterString (void)
        SCR_DrawCenterString ();
 }
 
+void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, int graphheight, float graphscale, const char *label, float textsize, int packetcounter, netgraphitem_t *netgraph)
+{
+       netgraphitem_t *graph;
+       int j, x, y;
+       int totalbytes = 0;
+       char bytesstring[128];
+       float g[NETGRAPH_PACKETS][6];
+       float *a;
+       float *b;
+       DrawQ_Fill(graphx, graphy, graphwidth, graphheight + textsize * 2, 0, 0, 0, 0.5, 0);
+       // draw the bar graph itself
+       // advance the packet counter because it is the latest packet column being
+       // built up and should come last
+       packetcounter = (packetcounter + 1) % NETGRAPH_PACKETS;
+       memset(g, 0, sizeof(g));
+       for (j = 0;j < NETGRAPH_PACKETS;j++)
+       {
+               graph = netgraph + j;
+               g[j][0] = 1.0f - 0.25f * (realtime - graph->time);
+               g[j][1] = 1.0f;
+               g[j][2] = 1.0f;
+               g[j][3] = 1.0f;
+               g[j][4] = 1.0f;
+               g[j][5] = 1.0f;
+               if (graph->unreliablebytes == NETGRAPH_LOSTPACKET)
+                       g[j][1] = 0.00f;
+               else if (graph->unreliablebytes == NETGRAPH_CHOKEDPACKET)
+                       g[j][2] = 0.96f;
+               else
+               {
+                       g[j][3] = 1.0f    - graph->unreliablebytes * graphscale;
+                       g[j][4] = g[j][3] - graph->reliablebytes   * graphscale;
+                       g[j][5] = g[j][4] - graph->ackbytes        * graphscale;
+                       // count bytes in the last second
+                       if (realtime - graph->time < 1.0f)
+                               totalbytes += graph->unreliablebytes + graph->reliablebytes + graph->ackbytes;
+               }
+               g[j][1] = bound(0.0f, g[j][1], 1.0f);
+               g[j][2] = bound(0.0f, g[j][2], 1.0f);
+               g[j][3] = bound(0.0f, g[j][3], 1.0f);
+               g[j][4] = bound(0.0f, g[j][4], 1.0f);
+               g[j][5] = bound(0.0f, g[j][5], 1.0f);
+       }
+       // render the lines for the graph
+       for (j = 0;j < NETGRAPH_PACKETS;j++)
+       {
+               a = g[j];
+               b = g[(j+1)%NETGRAPH_PACKETS];
+               if (a[0] < 0.0f || b[0] > 1.0f || b[0] < a[0])
+                       continue;
+               DrawQ_Line(0.0f, graphx + graphwidth * a[0], graphy + graphheight * a[2], graphx + graphwidth * b[0], graphy + graphheight * b[2], 1.0f, 1.0f, 0.0f, 1.0f, 0);
+               DrawQ_Line(0.0f, graphx + graphwidth * a[0], graphy + graphheight * a[1], graphx + graphwidth * b[0], graphy + graphheight * b[1], 1.0f, 0.0f, 0.0f, 1.0f, 0);
+               DrawQ_Line(0.0f, graphx + graphwidth * a[0], graphy + graphheight * a[5], graphx + graphwidth * b[0], graphy + graphheight * b[5], 0.0f, 1.0f, 0.0f, 1.0f, 0);
+               DrawQ_Line(0.0f, graphx + graphwidth * a[0], graphy + graphheight * a[4], graphx + graphwidth * b[0], graphy + graphheight * b[4], 1.0f, 1.0f, 1.0f, 1.0f, 0);
+               DrawQ_Line(0.0f, graphx + graphwidth * a[0], graphy + graphheight * a[3], graphx + graphwidth * b[0], graphy + graphheight * b[3], 1.0f, 0.5f, 0.0f, 1.0f, 0);
+       }
+       x = graphx;
+       y = graphy + graphheight;
+       dpsnprintf(bytesstring, sizeof(bytesstring), "%i", totalbytes);
+       DrawQ_String(x, y, label      , 0, textsize, textsize, 1.0f, 1.0f, 1.0f, 1.0f, 0, NULL, false, FONT_DEFAULT);y += textsize;
+       DrawQ_String(x, y, bytesstring, 0, textsize, textsize, 1.0f, 1.0f, 1.0f, 1.0f, 0, NULL, false, FONT_DEFAULT);y += textsize;
+}
+
+/*
+==============
+SCR_DrawNetGraph
+==============
+*/
+void SCR_DrawNetGraph (void)
+{
+       int i, separator1, separator2, graphwidth, graphheight, netgraph_x, netgraph_y, textsize, index, netgraphsperrow;
+       float graphscale;
+       netconn_t *c;
+
+       if (cls.state != ca_connected)
+               return;
+       if (!cls.netcon)
+               return;
+       if (!shownetgraph.integer)
+               return;
+
+       separator1 = 2;
+       separator2 = 4;
+       textsize = 8;
+       graphwidth = 120;
+       graphheight = 70;
+       graphscale = 1.0f / 1500.0f;
+
+       netgraphsperrow = (vid_conwidth.integer + separator2) / (graphwidth * 2 + separator1 + separator2);
+       netgraphsperrow = max(netgraphsperrow, 1);
+
+       index = 0;
+       netgraph_x = (vid_conwidth.integer + separator2) - (1 + (index % netgraphsperrow)) * (graphwidth * 2 + separator1 + separator2);
+       netgraph_y = (vid_conheight.integer - 48 - sbar_info_pos.integer + separator2) - (1 + (index / netgraphsperrow)) * (graphheight + textsize + separator2);
+       c = cls.netcon;
+       SCR_DrawNetGraph_DrawGraph(netgraph_x                          , netgraph_y, graphwidth, graphheight, graphscale, "incoming", textsize, c->incoming_packetcounter, c->incoming_netgraph);
+       SCR_DrawNetGraph_DrawGraph(netgraph_x + graphwidth + separator1, netgraph_y, graphwidth, graphheight, graphscale, "outgoing", textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
+       index++;
+
+       if (sv.active && shownetgraph.integer >= 2)
+       {
+               for (i = 0;i < svs.maxclients;i++)
+               {
+                       c = svs.clients[i].netconnection;
+                       if (!c)
+                               continue;
+                       netgraph_x = (vid_conwidth.integer + separator2) - (1 + (index % netgraphsperrow)) * (graphwidth * 2 + separator1 + separator2);
+                       netgraph_y = (vid_conheight.integer - 48 + separator2) - (1 + (index / netgraphsperrow)) * (graphheight + textsize + separator2);
+                       SCR_DrawNetGraph_DrawGraph(netgraph_x                          , netgraph_y, graphwidth, graphheight, graphscale, va("%s", svs.clients[i].name), textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
+                       SCR_DrawNetGraph_DrawGraph(netgraph_x + graphwidth + separator1, netgraph_y, graphwidth, graphheight, graphscale, ""                           , textsize, c->incoming_packetcounter, c->incoming_netgraph);
+                       index++;
+               }
+       }
+}
+
 /*
 ==============
 SCR_DrawTurtle
@@ -186,7 +344,7 @@ void SCR_DrawTurtle (void)
        if (!scr_showturtle.integer)
                return;
 
-       if (host_frametime < 0.1)
+       if (cl.realframetime < 0.1)
        {
                count = 0;
                return;
@@ -196,7 +354,7 @@ void SCR_DrawTurtle (void)
        if (count < 3)
                return;
 
-       DrawQ_Pic (0, 0, Draw_CachePic("gfx/turtle", true), 0, 0, 1, 1, 1, 1, 0);
+       DrawQ_Pic (0, 0, Draw_CachePic ("gfx/turtle"), 0, 0, 1, 1, 1, 1, 0);
 }
 
 /*
@@ -213,7 +371,7 @@ void SCR_DrawNet (void)
        if (cls.demoplayback)
                return;
 
-       DrawQ_Pic (64, 0, Draw_CachePic("gfx/net", true), 0, 0, 1, 1, 1, 1, 0);
+       DrawQ_Pic (64, 0, Draw_CachePic ("gfx/net"), 0, 0, 1, 1, 1, 1, 0);
 }
 
 /*
@@ -234,7 +392,7 @@ void SCR_DrawPause (void)
        if (!cl.paused)
                return;
 
-       pic = Draw_CachePic ("gfx/pause", true);
+       pic = Draw_CachePic ("gfx/pause");
        DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, (vid_conheight.integer - pic->height)/2, pic, 0, 0, 1, 1, 1, 1, 0);
 }
 
@@ -251,7 +409,7 @@ void SCR_DrawBrand (void)
        if (!scr_showbrand.value)
                return;
 
-       pic = Draw_CachePic ("gfx/brand", true);
+       pic = Draw_CachePic ("gfx/brand");
 
        switch ((int)scr_showbrand.value)
        {
@@ -296,27 +454,169 @@ void SCR_DrawBrand (void)
 
 /*
 ==============
-SCR_DrawDownload
+SCR_DrawQWDownload
 ==============
 */
-static void SCR_DrawDownload(void)
+static int SCR_DrawQWDownload(int offset)
 {
+       // sync with SCR_InfobarHeight
        int len;
        float x, y;
        float size = 8;
        char temp[256];
+
        if (!cls.qw_downloadname[0])
-               return;
-       dpsnprintf(temp, sizeof(temp), "Downloading %s ...  %3i%%\n", cls.qw_downloadname, cls.qw_downloadpercent);
+       {
+               cls.qw_downloadspeedrate = 0;
+               cls.qw_downloadspeedtime = realtime;
+               cls.qw_downloadspeedcount = 0;
+               return 0;
+       }
+       if (realtime >= cls.qw_downloadspeedtime + 1)
+       {
+               cls.qw_downloadspeedrate = cls.qw_downloadspeedcount;
+               cls.qw_downloadspeedtime = realtime;
+               cls.qw_downloadspeedcount = 0;
+       }
+       if (cls.protocol == PROTOCOL_QUAKEWORLD)
+               dpsnprintf(temp, sizeof(temp), "Downloading %s %3i%% (%i) at %i bytes/s\n", cls.qw_downloadname, cls.qw_downloadpercent, cls.qw_downloadmemorycursize, cls.qw_downloadspeedrate);
+       else
+               dpsnprintf(temp, sizeof(temp), "Downloading %s %3i%% (%i/%i) at %i bytes/s\n", cls.qw_downloadname, cls.qw_downloadpercent, cls.qw_downloadmemorycursize, cls.qw_downloadmemorymaxsize, cls.qw_downloadspeedrate);
        len = (int)strlen(temp);
-       x = (vid_conwidth.integer - len*size) / 2;
-       y = vid_conheight.integer - size;
-       DrawQ_Pic(0, y, NULL, vid_conwidth.integer, size, 0, 0, 0, 0.5, 0);
-       DrawQ_String(x, y, temp, len, size, size, 1, 1, 1, 1, 0);
+       x = (vid_conwidth.integer - DrawQ_TextWidth(temp, len, size, size, true, FONT_INFOBAR)) / 2;
+       y = vid_conheight.integer - size - offset;
+       DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, cls.signon == SIGNONS ? 0.5 : 1, 0);
+       DrawQ_String(x, y, temp, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
+       return 8;
 }
+/*
+==============
+SCR_DrawInfobarString
+==============
+*/
+static int SCR_DrawInfobarString(int offset)
+{
+       int len;
+       float x, y;
+       float size = 8;
 
-//=============================================================================
+       len = (int)strlen(scr_infobarstring);
+       x = (vid_conwidth.integer - DrawQ_TextWidth(scr_infobarstring, len, size, size, false, FONT_INFOBAR)) / 2;
+       y = vid_conheight.integer - size - offset;
+       DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, cls.signon == SIGNONS ? 0.5 : 1, 0);
+       DrawQ_String(x, y, scr_infobarstring, len, size, size, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
+       return 8;
+}
 
+/*
+==============
+SCR_DrawCurlDownload
+==============
+*/
+static int SCR_DrawCurlDownload(int offset)
+{
+       // sync with SCR_InfobarHeight
+       int len;
+       int nDownloads;
+       int i;
+       float x, y;
+       float size = 8;
+       Curl_downloadinfo_t *downinfo;
+       char temp[256];
+       const char *addinfo;
+
+       downinfo = Curl_GetDownloadInfo(&nDownloads, &addinfo);
+       if(!downinfo)
+               return 0;
+
+       y = vid_conheight.integer - size * nDownloads - offset;
+
+       if(addinfo)
+       {
+               len = (int)strlen(addinfo);
+               x = (vid_conwidth.integer - DrawQ_TextWidth(addinfo, len, size, size, true, FONT_INFOBAR)) / 2;
+               DrawQ_Fill(0, y - size, vid_conwidth.integer, size, 1, 1, 1, cls.signon == SIGNONS ? 0.8 : 1, 0);
+               DrawQ_String(x, y - size, addinfo, len, size, size, 0, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
+       }
+
+       for(i = 0; i != nDownloads; ++i)
+       {
+               if(downinfo[i].queued)
+                       dpsnprintf(temp, sizeof(temp), "Still in queue: %s\n", downinfo[i].filename);
+               else if(downinfo[i].progress <= 0)
+                       dpsnprintf(temp, sizeof(temp), "Downloading %s ...  ???.?%% @ %.1f KiB/s\n", downinfo[i].filename, downinfo[i].speed / 1024.0);
+               else
+                       dpsnprintf(temp, sizeof(temp), "Downloading %s ...  %5.1f%% @ %.1f KiB/s\n", downinfo[i].filename, 100.0 * downinfo[i].progress, downinfo[i].speed / 1024.0);
+               len = (int)strlen(temp);
+               x = (vid_conwidth.integer - DrawQ_TextWidth(temp, len, size, size, true, FONT_INFOBAR)) / 2;
+               DrawQ_Fill(0, y + i * size, vid_conwidth.integer, size, 0, 0, 0, cls.signon == SIGNONS ? 0.5 : 1, 0);
+               DrawQ_String(x, y + i * size, temp, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
+       }
+
+       Z_Free(downinfo);
+
+       return 8 * (nDownloads + (addinfo ? 1 : 0));
+}
+
+/*
+==============
+SCR_DrawInfobar
+==============
+*/
+static void SCR_DrawInfobar(void)
+{
+       int offset = 0;
+       if(scr_infobartime_off > 0)
+               offset += SCR_DrawInfobarString(offset);
+       offset += SCR_DrawQWDownload(offset);
+       offset += SCR_DrawCurlDownload(offset);
+       if(offset != scr_con_margin_bottom)
+               Con_DPrintf("broken console margin calculation: %d != %d\n", offset, scr_con_margin_bottom);
+}
+
+static int SCR_InfobarHeight(void)
+{
+       int offset = 0;
+       Curl_downloadinfo_t *downinfo;
+       const char *addinfo;
+       int nDownloads;
+
+       if (cl.time > cl.oldtime)
+               scr_infobartime_off -= cl.time - cl.oldtime;
+       if(scr_infobartime_off > 0)
+               offset += 8;
+
+       if(cls.qw_downloadname[0])
+               offset += 8;
+
+       downinfo = Curl_GetDownloadInfo(&nDownloads, &addinfo);
+       if(downinfo)
+       {
+               offset += 8 * (nDownloads + (addinfo ? 1 : 0));
+               Z_Free(downinfo);
+       }
+
+       return offset;
+}
+
+/*
+==============
+SCR_InfoBar_f
+==============
+*/
+void SCR_InfoBar_f(void)
+{
+       if(Cmd_Argc() == 3)
+       {
+               scr_infobartime_off = atof(Cmd_Argv(1));
+               strlcpy(scr_infobarstring, Cmd_Argv(2), sizeof(scr_infobarstring));
+       }
+       else
+       {
+               Con_Printf("usage:\ninfobar expiretime \"string\"\n");
+       }
+}
+//=============================================================================
 
 /*
 ==================
@@ -334,7 +634,7 @@ void SCR_SetUpToDrawConsole (void)
        if (scr_menuforcewhiledisconnected.integer && key_dest == key_game && cls.state == ca_disconnected)
        {
                if (framecounter >= 2)
-                       MR_ToggleMenu_f();
+                       MR_ToggleMenu(1);
                else
                        framecounter++;
        }
@@ -352,24 +652,7 @@ void SCR_SetUpToDrawConsole (void)
        else
                conlines = 0;                           // none visible
 
-       if (scr_conspeed.value)
-       {
-               if (scr_con_current > conlines)
-               {
-                       scr_con_current -= scr_conspeed.value*host_realframetime;
-                       if (scr_con_current < conlines)
-                               scr_con_current = conlines;
-
-               }
-               else if (scr_con_current < conlines)
-               {
-                       scr_con_current += scr_conspeed.value*host_realframetime;
-                       if (scr_con_current > conlines)
-                               scr_con_current = conlines;
-               }
-       }
-       else
-               scr_con_current = conlines;
+       scr_con_current = conlines;
 }
 
 /*
@@ -379,19 +662,16 @@ SCR_DrawConsole
 */
 void SCR_DrawConsole (void)
 {
+       scr_con_margin_bottom = SCR_InfobarHeight();
        if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
        {
                // full screen
-               Con_DrawConsole (vid_conheight.integer);
+               Con_DrawConsole (vid_conheight.integer - scr_con_margin_bottom);
        }
        else if (scr_con_current)
-               Con_DrawConsole (scr_con_current);
+               Con_DrawConsole (min((int)scr_con_current, vid_conheight.integer - scr_con_margin_bottom));
        else
-       {
                con_vislines = 0;
-               if (key_dest == key_game || key_dest == key_message)
-                       Con_DrawNotify ();      // only draw notify in game
-       }
 }
 
 /*
@@ -406,15 +686,15 @@ void SCR_BeginLoadingPlaque (void)
        Log_Start();
 
        Host_StartVideo();
-       S_StopAllSounds();
-       SCR_UpdateLoadingScreen();
+       SCR_UpdateLoadingScreen(false);
 }
 
 //=============================================================================
 
-char r_speeds_string[1024];
+char r_speeds_timestring[4096];
 int speedstringcount, r_timereport_active;
 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
+int r_speeds_longestitem = 0;
 
 void R_TimeReport(char *desc)
 {
@@ -425,77 +705,126 @@ void R_TimeReport(char *desc)
        if (r_speeds.integer < 2 || !r_timereport_active)
                return;
 
-       qglFinish();
+       CHECKGLERROR
+       if (r_speeds.integer == 2 && qglFinish)
+               qglFinish();
+       CHECKGLERROR
        r_timereport_temp = r_timereport_current;
        r_timereport_current = Sys_DoubleTime();
        t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0 + 0.5);
 
-       dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %-11s", t, desc);
-       length = (int)strlen(tempbuf);
+       length = dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %s", t, desc);
+       length = min(length, (int)sizeof(tempbuf) - 1);
+       if (r_speeds_longestitem < length)
+               r_speeds_longestitem = length;
+       for (;length < r_speeds_longestitem;length++)
+               tempbuf[length] = ' ';
+       tempbuf[length] = 0;
+
        if (speedstringcount + length > (vid_conwidth.integer / 8))
        {
-               strlcat(r_speeds_string, "\n", sizeof(r_speeds_string));
+               strlcat(r_speeds_timestring, "\n", sizeof(r_speeds_timestring));
                speedstringcount = 0;
        }
-       strlcat(r_speeds_string, tempbuf, sizeof(r_speeds_string));
+       strlcat(r_speeds_timestring, tempbuf, sizeof(r_speeds_timestring));
        speedstringcount += length;
 }
 
-void R_TimeReport_Frame(void)
+void R_TimeReport_BeginFrame(void)
+{
+       speedstringcount = 0;
+       r_speeds_timestring[0] = 0;
+       r_timereport_active = false;
+       memset(&r_refdef.stats, 0, sizeof(r_refdef.stats));
+
+       if (r_speeds.integer >= 2 && cls.signon == SIGNONS && cls.state == ca_connected)
+       {
+               r_timereport_active = true;
+               r_timereport_start = r_timereport_current = Sys_DoubleTime();
+       }
+}
+
+static int R_CountLeafTriangles(const dp_model_t *model, const mleaf_t *leaf)
+{
+       int i, triangles = 0;
+       for (i = 0;i < leaf->numleafsurfaces;i++)
+               triangles += model->data_surfaces[leaf->firstleafsurface[i]].num_triangles;
+       return triangles;
+}
+
+void R_TimeReport_EndFrame(void)
 {
        int i, j, lines, y;
+       cl_locnode_t *loc;
+       char string[1024+4096];
+       mleaf_t *viewleaf;
 
-       if (r_speeds_string[0])
+       string[0] = 0;
+       if (r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected)
        {
-               if (r_timereport_active)
+               // put the location name in the r_speeds display as it greatly helps
+               // when creating loc files
+               loc = CL_Locs_FindNearest(cl.movement_origin);
+               viewleaf = (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf) ? r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, r_refdef.view.origin) : NULL;
+               dpsnprintf(string, sizeof(string),
+"%s%s\n"
+"%3i renders org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n"
+"%5i viewleaf%5i cluster%2i area%4i brushes%4i surfaces(%7i triangles)\n"
+"%7i surfaces%7i triangles %5i entities (%7i surfaces%7i triangles)\n"
+"%5i leafs%5i portals%6i/%6i particles%6i/%6i decals %3i%% quality\n"
+"%7i lightmap updates (%7i pixels)\n"
+"%4i lights%4i clears%4i scissored%7i light%7i shadow%7i dynamic\n"
+"rendered%6i meshes%8i triangles bloompixels%8i copied%8i drawn\n"
+"updated%5i indexbuffers%8i bytes%5i vertexbuffers%8i bytes\n"
+"%s"
+, loc ? "Location: " : "", loc ? loc->name : ""
+, r_refdef.stats.renders, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], r_refdef.view.forward[0], r_refdef.view.forward[1], r_refdef.view.forward[2]
+, viewleaf ? (int)(viewleaf - r_refdef.scene.worldmodel->brush.data_leafs) : -1, viewleaf ? viewleaf->clusterindex : -1, viewleaf ? viewleaf->areaindex : -1, viewleaf ? viewleaf->numleafbrushes : 0, viewleaf ? viewleaf->numleafsurfaces : 0, viewleaf ? R_CountLeafTriangles(r_refdef.scene.worldmodel, viewleaf) : 0
+, r_refdef.stats.world_surfaces, r_refdef.stats.world_triangles, r_refdef.stats.entities, r_refdef.stats.entities_surfaces, r_refdef.stats.entities_triangles
+, r_refdef.stats.world_leafs, r_refdef.stats.world_portals, r_refdef.stats.particles, cl.num_particles, r_refdef.stats.drawndecals, r_refdef.stats.totaldecals, (int)(100 * r_refdef.view.quality)
+, r_refdef.stats.lightmapupdates, r_refdef.stats.lightmapupdatepixels
+, r_refdef.stats.lights, r_refdef.stats.lights_clears, r_refdef.stats.lights_scissored, r_refdef.stats.lights_lighttriangles, r_refdef.stats.lights_shadowtriangles, r_refdef.stats.lights_dynamicshadowtriangles
+, r_refdef.stats.meshes, r_refdef.stats.meshes_elements / 3, r_refdef.stats.bloom_copypixels, r_refdef.stats.bloom_drawpixels
+, r_refdef.stats.indexbufferuploadcount, r_refdef.stats.indexbufferuploadsize, r_refdef.stats.vertexbufferuploadcount, r_refdef.stats.vertexbufferuploadsize
+, r_speeds_timestring);
+
+               memset(&r_refdef.stats, 0, sizeof(r_refdef.stats));
+
+               speedstringcount = 0;
+               r_speeds_timestring[0] = 0;
+               r_timereport_active = false;
+
+               if (r_speeds.integer >= 2)
                {
-                       r_timereport_current = r_timereport_start;
-                       R_TimeReport("total");
+                       r_timereport_active = true;
+                       r_timereport_start = r_timereport_current = Sys_DoubleTime();
                }
+       }
 
-               if (r_speeds_string[strlen(r_speeds_string)-1] == '\n')
-                       r_speeds_string[strlen(r_speeds_string)-1] = 0;
+       if (string[0])
+       {
+               if (string[strlen(string)-1] == '\n')
+                       string[strlen(string)-1] = 0;
                lines = 1;
-               for (i = 0;r_speeds_string[i];i++)
-                       if (r_speeds_string[i] == '\n')
+               for (i = 0;string[i];i++)
+                       if (string[i] == '\n')
                                lines++;
                y = vid_conheight.integer - sb_lines - lines * 8;
                i = j = 0;
-               DrawQ_Pic(0, y, NULL, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
-               while (r_speeds_string[i])
+               r_draw2d_force = true;
+               DrawQ_Fill(0, y, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
+               while (string[i])
                {
                        j = i;
-                       while (r_speeds_string[i] && r_speeds_string[i] != '\n')
+                       while (string[i] && string[i] != '\n')
                                i++;
                        if (i - j > 0)
-                               DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
-                       if (r_speeds_string[i] == '\n')
+                               DrawQ_String(0, y, string + j, i - j, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT);
+                       if (string[i] == '\n')
                                i++;
                        y += 8;
                }
-               r_speeds_string[0] = 0;
-               r_timereport_active = false;
-       }
-       if (r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected)
-       {
-               speedstringcount = 0;
-               r_speeds_string[0] = 0;
-               r_timereport_active = false;
-               sprintf(r_speeds_string + strlen(r_speeds_string), "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n", r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2]);
-               sprintf(r_speeds_string + strlen(r_speeds_string), "%5i entities%6i surfaces%6i triangles%5i leafs%5i portals%6i particles\n", renderstats.entities, renderstats.entities_surfaces, renderstats.entities_triangles, renderstats.world_leafs, renderstats.world_portals, renderstats.particles);
-               sprintf(r_speeds_string + strlen(r_speeds_string), "%4i lights%4i clears%4i scissored%7i light%7i shadow%7i dynamic\n", renderstats.lights, renderstats.lights_clears, renderstats.lights_scissored, renderstats.lights_lighttriangles, renderstats.lights_shadowtriangles, renderstats.lights_dynamicshadowtriangles);
-               if (renderstats.bloom)
-                       sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles bloompixels%8i copied%8i drawn\n", renderstats.meshes, renderstats.meshes_elements / 3, renderstats.bloom_copypixels, renderstats.bloom_drawpixels);
-               else
-                       sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles\n", renderstats.meshes, renderstats.meshes_elements / 3);
-
-               memset(&renderstats, 0, sizeof(renderstats));
-
-               if (r_speeds.integer >= 2)
-               {
-                       r_timereport_active = true;
-                       r_timereport_start = r_timereport_current = Sys_DoubleTime();
-               }
+               r_draw2d_force = false;
        }
 }
 
@@ -524,15 +853,33 @@ void SCR_SizeDown_f (void)
        Cvar_SetValue ("viewsize",scr_viewsize.value-10);
 }
 
+void SCR_CaptureVideo_EndVideo(void);
+void CL_Screen_Shutdown(void)
+{
+       SCR_CaptureVideo_EndVideo();
+}
+
 void CL_Screen_Init(void)
 {
        Cvar_RegisterVariable (&scr_fov);
        Cvar_RegisterVariable (&scr_viewsize);
-       Cvar_RegisterVariable (&scr_conspeed);
        Cvar_RegisterVariable (&scr_conalpha);
+       Cvar_RegisterVariable (&scr_conalphafactor);
+       Cvar_RegisterVariable (&scr_conalpha2factor);
+       Cvar_RegisterVariable (&scr_conalpha3factor);
+       Cvar_RegisterVariable (&scr_conscroll_x);
+       Cvar_RegisterVariable (&scr_conscroll_y);
+       Cvar_RegisterVariable (&scr_conscroll2_x);
+       Cvar_RegisterVariable (&scr_conscroll2_y);
+       Cvar_RegisterVariable (&scr_conscroll3_x);
+       Cvar_RegisterVariable (&scr_conscroll3_y);
        Cvar_RegisterVariable (&scr_conbrightness);
        Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
        Cvar_RegisterVariable (&scr_menuforcewhiledisconnected);
+       Cvar_RegisterVariable (&scr_loadingscreen_background);
+       Cvar_RegisterVariable (&scr_loadingscreen_count);
+       Cvar_RegisterVariable (&scr_loadingscreen_barcolor);
+       Cvar_RegisterVariable (&scr_loadingscreen_barheight);
        Cvar_RegisterVariable (&scr_showram);
        Cvar_RegisterVariable (&scr_showturtle);
        Cvar_RegisterVariable (&scr_showpause);
@@ -544,27 +891,45 @@ void CL_Screen_Init(void)
        Cvar_RegisterVariable (&vid_pixelheight);
        Cvar_RegisterVariable (&scr_screenshot_jpeg);
        Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
-       Cvar_RegisterVariable (&scr_screenshot_gamma);
+       Cvar_RegisterVariable (&scr_screenshot_png);
+       Cvar_RegisterVariable (&scr_screenshot_gammaboost);
+       Cvar_RegisterVariable (&scr_screenshot_hwgamma);
+       Cvar_RegisterVariable (&scr_screenshot_name_in_mapdir);
+       Cvar_RegisterVariable (&scr_screenshot_alpha);
        Cvar_RegisterVariable (&cl_capturevideo);
-       Cvar_RegisterVariable (&cl_capturevideo_sound);
+       Cvar_RegisterVariable (&cl_capturevideo_printfps);
+       Cvar_RegisterVariable (&cl_capturevideo_width);
+       Cvar_RegisterVariable (&cl_capturevideo_height);
+       Cvar_RegisterVariable (&cl_capturevideo_realtime);
        Cvar_RegisterVariable (&cl_capturevideo_fps);
-       Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
-       Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
+       Cvar_RegisterVariable (&cl_capturevideo_nameformat);
+       Cvar_RegisterVariable (&cl_capturevideo_number);
+       Cvar_RegisterVariable (&cl_capturevideo_ogg);
+       Cvar_RegisterVariable (&cl_capturevideo_framestep);
        Cvar_RegisterVariable (&r_letterbox);
        Cvar_RegisterVariable(&r_stereo_separation);
        Cvar_RegisterVariable(&r_stereo_sidebyside);
        Cvar_RegisterVariable(&r_stereo_redblue);
        Cvar_RegisterVariable(&r_stereo_redcyan);
        Cvar_RegisterVariable(&r_stereo_redgreen);
+       Cvar_RegisterVariable(&r_stereo_angle);
        Cvar_RegisterVariable(&scr_zoomwindow);
        Cvar_RegisterVariable(&scr_zoomwindow_viewsizex);
        Cvar_RegisterVariable(&scr_zoomwindow_viewsizey);
        Cvar_RegisterVariable(&scr_zoomwindow_fov);
+       Cvar_RegisterVariable(&scr_stipple);
+       Cvar_RegisterVariable(&scr_refresh);
+       Cvar_RegisterVariable(&shownetgraph);
+       Cvar_RegisterVariable(&cl_demo_mousegrab);
+       Cvar_RegisterVariable(&timedemo_screenshotframelist);
 
        Cmd_AddCommand ("sizeup",SCR_SizeUp_f, "increase view size (increases viewsize cvar)");
        Cmd_AddCommand ("sizedown",SCR_SizeDown_f, "decrease view size (decreases viewsize cvar)");
        Cmd_AddCommand ("screenshot",SCR_ScreenShot_f, "takes a screenshot of the next rendered frame");
        Cmd_AddCommand ("envmap", R_Envmap_f, "render a cubemap (skybox) of the current scene");
+       Cmd_AddCommand ("infobar", SCR_InfoBar_f, "display a text in the infobar (usage: infobar expiretime string)");
+
+       SCR_CaptureVideo_Ogg_Init();
 
        scr_initialized = true;
 }
@@ -577,73 +942,141 @@ SCR_ScreenShot_f
 void SCR_ScreenShot_f (void)
 {
        static int shotnumber;
-       static char oldname[MAX_QPATH];
-       char base[MAX_QPATH];
+       static char old_prefix_name[MAX_QPATH];
+       char prefix_name[MAX_QPATH];
        char filename[MAX_QPATH];
        unsigned char *buffer1;
        unsigned char *buffer2;
-       unsigned char *buffer3;
        qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
+       qboolean png = (scr_screenshot_png.integer != 0) && !jpeg;
 
-       sprintf (base, "screenshots/%s", scr_screenshot_name.string);
-
-       if (strcmp (oldname, scr_screenshot_name.string))
+       if (Cmd_Argc() == 2)
        {
-               sprintf(oldname, "%s", scr_screenshot_name.string);
-               shotnumber = 0;
+               const char *ext;
+               strlcpy(filename, Cmd_Argv(1), sizeof(filename));
+               ext = FS_FileExtension(filename);
+               if (!strcasecmp(ext, "jpg"))
+               {
+                       jpeg = true;
+                       png = false;
+               }
+               else if (!strcasecmp(ext, "tga"))
+               {
+                       jpeg = false;
+                       png = false;
+               }
+               else if (!strcasecmp(ext, "png"))
+               {
+                       jpeg = false;
+                       png = true;
+               }
+               else
+               {
+                       Con_Printf("screenshot: supplied filename must end in .jpg or .tga or .png\n");
+                       return;
+               }
        }
-
-       // find a file name to save it to
-       for (;shotnumber < 1000000;shotnumber++)
-               if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
-                       break;
-       if (shotnumber >= 1000000)
+       else
        {
-               Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
-               return;
-       }
+               // TODO maybe make capturevideo and screenshot use similar name patterns?
+               if (scr_screenshot_name_in_mapdir.integer && cl.worldbasename[0])
+                       dpsnprintf (prefix_name, sizeof(prefix_name), "%s/%s", cl.worldbasename, Sys_TimeString(scr_screenshot_name.string));
+               else
+                       dpsnprintf (prefix_name, sizeof(prefix_name), "%s", Sys_TimeString(scr_screenshot_name.string));
+
+               if (strcmp(old_prefix_name, prefix_name))
+               {
+                       dpsnprintf(old_prefix_name, sizeof(old_prefix_name), "%s", prefix_name );
+                       shotnumber = 0;
+               }
 
-       sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
+               // find a file name to save it to
+               for (;shotnumber < 1000000;shotnumber++)
+                       if (!FS_SysFileExists(va("%s/screenshots/%s%06d.tga", fs_gamedir, prefix_name, shotnumber)) && !FS_SysFileExists(va("%s/screenshots/%s%06d.jpg", fs_gamedir, prefix_name, shotnumber)) && !FS_SysFileExists(va("%s/screenshots/%s%06d.png", fs_gamedir, prefix_name, shotnumber)))
+                               break;
+               if (shotnumber >= 1000000)
+               {
+                       Con_Print("Couldn't create the image file\n");
+                       return;
+               }
 
-       buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
-       buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
-       buffer3 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
+               dpsnprintf(filename, sizeof(filename), "screenshots/%s%06d.%s", prefix_name, shotnumber, jpeg ? "jpg" : png ? "png" : "tga");
+       }
+
+       buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 4);
+       buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * (scr_screenshot_alpha.integer ? 4 : 3));
 
-       if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
+       if (SCR_ScreenShot (filename, buffer1, buffer2, 0, 0, vid.width, vid.height, false, false, false, jpeg, png, true, scr_screenshot_alpha.integer != 0))
                Con_Printf("Wrote %s\n", filename);
        else
-               Con_Printf("unable to write %s\n", filename);
+       {
+               Con_Printf("Unable to write %s\n", filename);
+               if(jpeg || png)
+               {
+                       if(SCR_ScreenShot (filename, buffer1, buffer2, 0, 0, vid.width, vid.height, false, false, false, false, false, true, scr_screenshot_alpha.integer != 0))
+                       {
+                               strlcpy(filename + strlen(filename) - 3, "tga", 4);
+                               Con_Printf("Wrote %s\n", filename);
+                       }
+               }
+       }
 
        Mem_Free (buffer1);
        Mem_Free (buffer2);
-       Mem_Free (buffer3);
 
        shotnumber++;
 }
 
 void SCR_CaptureVideo_BeginVideo(void)
 {
-       double gamma, g;
+       double r, g, b;
        unsigned int i;
-       unsigned char out[44];
-       if (cls.capturevideo_active)
+       int width = cl_capturevideo_width.integer, height = cl_capturevideo_height.integer;
+       if (cls.capturevideo.active)
                return;
+       memset(&cls.capturevideo, 0, sizeof(cls.capturevideo));
        // soundrate is figured out on the first SoundFrame
-       cls.capturevideo_active = true;
-       cls.capturevideo_starttime = Sys_DoubleTime();
-       cls.capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
-       cls.capturevideo_soundrate = 0;
-       cls.capturevideo_frame = 0;
-       cls.capturevideo_buffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
-       gamma = 1.0/scr_screenshot_gamma.value;
+
+       if(width == 0 && height != 0)
+               width = (int) (height * (double)vid.width / ((double)vid.height * vid_pixelheight.value)); // keep aspect
+       if(width != 0 && height == 0)
+               height = (int) (width * ((double)vid.height * vid_pixelheight.value) / (double)vid.width); // keep aspect
+
+       if(width < 2 || width > vid.width) // can't scale up
+               width = vid.width;
+       if(height < 2 || height > vid.height) // can't scale up
+               height = vid.height;
+
+       // ensure it's all even; if not, scale down a little
+       if(width % 1)
+               --width;
+       if(height % 1)
+               --height;
+
+       cls.capturevideo.width = width;
+       cls.capturevideo.height = height;
+       cls.capturevideo.active = true;
+       cls.capturevideo.framerate = bound(1, cl_capturevideo_fps.value, 1001) * bound(1, cl_capturevideo_framestep.integer, 64);
+       cls.capturevideo.framestep = cl_capturevideo_framestep.integer;
+       cls.capturevideo.soundrate = S_GetSoundRate();
+       cls.capturevideo.soundchannels = S_GetSoundChannels();
+       cls.capturevideo.startrealtime = realtime;
+       cls.capturevideo.frame = cls.capturevideo.lastfpsframe = 0;
+       cls.capturevideo.starttime = cls.capturevideo.lastfpstime = Sys_DoubleTime();
+       cls.capturevideo.soundsampleframe = 0;
+       cls.capturevideo.realtime = cl_capturevideo_realtime.integer != 0;
+       cls.capturevideo.screenbuffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 4);
+       cls.capturevideo.outbuffer = (unsigned char *)Mem_Alloc(tempmempool, width * height * (4+4) + 18);
+       dpsnprintf(cls.capturevideo.basename, sizeof(cls.capturevideo.basename), "video/%s%03i", Sys_TimeString(cl_capturevideo_nameformat.string), cl_capturevideo_number.integer);
+       Cvar_SetValueQuick(&cl_capturevideo_number, cl_capturevideo_number.integer + 1);
 
        /*
        for (i = 0;i < 256;i++)
        {
                unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
-               cls.capturevideo_rgbgammatable[0][i] = j;
-               cls.capturevideo_rgbgammatable[1][i] = j;
-               cls.capturevideo_rgbgammatable[2][i] = j;
+               cls.capturevideo.rgbgammatable[0][i] = j;
+               cls.capturevideo.rgbgammatable[1][i] = j;
+               cls.capturevideo.rgbgammatable[2][i] = j;
        }
        */
 /*
@@ -654,264 +1087,220 @@ Y = R *  .299 + G *  .587 + B *  .114;
 Cb = R * -.169 + G * -.332 + B *  .500 + 128.;
 Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
 */
+
+       if(WANT_SCREENSHOT_HWGAMMA)
+       {
+               VID_BuildGammaTables(&cls.capturevideo.vidramp[0], 256);
+       }
+       else
+       {
+               // identity gamma table
+               BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, cls.capturevideo.vidramp, 256);
+               BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, cls.capturevideo.vidramp + 256, 256);
+               BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, cls.capturevideo.vidramp + 256*2, 256);
+       }
+       if(scr_screenshot_gammaboost.value != 1)
+       {
+               double igamma = 1 / scr_screenshot_gammaboost.value;
+               for (i = 0;i < 256 * 3;i++)
+                       cls.capturevideo.vidramp[i] = (unsigned short) (0.5 + pow(cls.capturevideo.vidramp[i] * (1.0 / 65535.0), igamma) * 65535.0);
+       }
+
        for (i = 0;i < 256;i++)
        {
-               g = 255*pow(i/255.0, gamma);
+               r = 255*cls.capturevideo.vidramp[i]/65535.0;
+               g = 255*cls.capturevideo.vidramp[i+256]/65535.0;
+               b = 255*cls.capturevideo.vidramp[i+512]/65535.0;
+               // NOTE: we have to round DOWN here, or integer overflows happen. Sorry for slightly wrong looking colors sometimes...
                // Y weights from RGB
-               cls.capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g *  0.299);
-               cls.capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g *  0.587);
-               cls.capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g *  0.114);
+               cls.capturevideo.rgbtoyuvscaletable[0][0][i] = (short)(r *  0.299);
+               cls.capturevideo.rgbtoyuvscaletable[0][1][i] = (short)(g *  0.587);
+               cls.capturevideo.rgbtoyuvscaletable[0][2][i] = (short)(b *  0.114);
                // Cb weights from RGB
-               cls.capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
-               cls.capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
-               cls.capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g *  0.500);
+               cls.capturevideo.rgbtoyuvscaletable[1][0][i] = (short)(r * -0.169);
+               cls.capturevideo.rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
+               cls.capturevideo.rgbtoyuvscaletable[1][2][i] = (short)(b *  0.500);
                // Cr weights from RGB
-               cls.capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g *  0.500);
-               cls.capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
-               cls.capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
+               cls.capturevideo.rgbtoyuvscaletable[2][0][i] = (short)(r *  0.500);
+               cls.capturevideo.rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
+               cls.capturevideo.rgbtoyuvscaletable[2][2][i] = (short)(b * -0.0813);
                // range reduction of YCbCr to valid signal range
-               cls.capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
-               cls.capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
-               cls.capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
+               cls.capturevideo.yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
+               cls.capturevideo.yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
+               cls.capturevideo.yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
        }
 
-       if (cl_capturevideo_rawrgb.integer)
+       if (cl_capturevideo_ogg.integer)
        {
-               cls.capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
-               cls.capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
-       }
-       else if (cl_capturevideo_rawyv12.integer)
-       {
-               cls.capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
-               cls.capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
+               if(SCR_CaptureVideo_Ogg_Available())
+               {
+                       SCR_CaptureVideo_Ogg_BeginVideo();
+                       return;
+               }
+               else
+                       Con_Print("cl_capturevideo_ogg: libraries not available. Capturing in AVI instead.\n");
        }
-       else if (scr_screenshot_jpeg.integer)
+
+       SCR_CaptureVideo_Avi_BeginVideo();
+}
+
+void SCR_CaptureVideo_EndVideo(void)
+{
+       if (!cls.capturevideo.active)
+               return;
+       cls.capturevideo.active = false;
+
+       Con_Printf("Finishing capture of %s.%s (%d frames, %d audio frames)\n", cls.capturevideo.basename, cls.capturevideo.formatextension, cls.capturevideo.frame, cls.capturevideo.soundsampleframe);
+
+       if (cls.capturevideo.videofile)
        {
-               cls.capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
-               cls.capturevideo_videofile = NULL;
+               cls.capturevideo.endvideo();
        }
-       else
+
+       if (cls.capturevideo.screenbuffer)
        {
-               cls.capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
-               cls.capturevideo_videofile = NULL;
+               Mem_Free (cls.capturevideo.screenbuffer);
+               cls.capturevideo.screenbuffer = NULL;
        }
 
-       if (cl_capturevideo_sound.integer)
+       if (cls.capturevideo.outbuffer)
        {
-               cls.capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
-               // wave header will be filled out when video ends
-               memset(out, 0, 44);
-               FS_Write (cls.capturevideo_soundfile, out, 44);
+               Mem_Free (cls.capturevideo.outbuffer);
+               cls.capturevideo.outbuffer = NULL;
        }
-       else
-               cls.capturevideo_soundfile = NULL;
+
+       memset(&cls.capturevideo, 0, sizeof(cls.capturevideo));
 }
 
-void SCR_CaptureVideo_EndVideo(void)
+static void SCR_ScaleDownBGRA(unsigned char *in, int inw, int inh, unsigned char *out, int outw, int outh)
 {
-       int i, n;
-       unsigned char out[44];
-       if (!cls.capturevideo_active)
+       // TODO optimize this function
+
+       int x, y;
+       float area;
+
+       // memcpy is faster than me
+       if(inw == outw && inh == outh)
+       {
+               memcpy(out, in, 4 * inw * inh);
                return;
-       cls.capturevideo_active = false;
-
-       if (cls.capturevideo_videofile)
-       {
-               FS_Close(cls.capturevideo_videofile);
-               cls.capturevideo_videofile = NULL;
-       }
-
-       // finish the wave file
-       if (cls.capturevideo_soundfile)
-       {
-               i = (int)FS_Tell (cls.capturevideo_soundfile);
-               //"RIFF", (int) unknown (chunk size), "WAVE",
-               //"fmt ", (int) 16 (chunk size), (short) format 1 (uncompressed PCM), (short) 2 channels, (int) unknown rate, (int) unknown bytes per second, (short) 4 bytes per sample (channels * bytes per channel), (short) 16 bits per channel
-               //"data", (int) unknown (chunk size)
-               memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
-               // the length of the whole RIFF chunk
-               n = i - 8;
-               out[4] = (n) & 0xFF;
-               out[5] = (n >> 8) & 0xFF;
-               out[6] = (n >> 16) & 0xFF;
-               out[7] = (n >> 24) & 0xFF;
-               // rate
-               n = cls.capturevideo_soundrate;
-               out[24] = (n) & 0xFF;
-               out[25] = (n >> 8) & 0xFF;
-               out[26] = (n >> 16) & 0xFF;
-               out[27] = (n >> 24) & 0xFF;
-               // bytes per second (rate * channels * bytes per channel)
-               n = cls.capturevideo_soundrate * 2 * 2;
-               out[28] = (n) & 0xFF;
-               out[29] = (n >> 8) & 0xFF;
-               out[30] = (n >> 16) & 0xFF;
-               out[31] = (n >> 24) & 0xFF;
-               // the length of the data chunk
-               n = i - 44;
-               out[40] = (n) & 0xFF;
-               out[41] = (n >> 8) & 0xFF;
-               out[42] = (n >> 16) & 0xFF;
-               out[43] = (n >> 24) & 0xFF;
-               FS_Seek (cls.capturevideo_soundfile, 0, SEEK_SET);
-               FS_Write (cls.capturevideo_soundfile, out, 44);
-               FS_Close (cls.capturevideo_soundfile);
-               cls.capturevideo_soundfile = NULL;
-       }
-
-       if (cls.capturevideo_buffer)
-       {
-               Mem_Free (cls.capturevideo_buffer);
-               cls.capturevideo_buffer = NULL;
-       }
-
-       cls.capturevideo_starttime = 0;
-       cls.capturevideo_framerate = 0;
-       cls.capturevideo_frame = 0;
-}
+       }
 
-qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
-{
-       int x = 0, y = 0, width = vid.width, height = vid.height;
-       unsigned char *b, *out;
-       char filename[32];
-       int outoffset = (width/2)*(height/2);
-       //return SCR_ScreenShot(filename, cls.capturevideo_buffer, cls.capturevideo_buffer + vid.width * vid.height * 3, cls.capturevideo_buffer + vid.width * vid.height * 6, 0, 0, vid.width, vid.height, false, false, false, jpeg, true);
-       // speed is critical here, so do saving as directly as possible
-       switch (cls.capturevideo_format)
+       // otherwise: a box filter
+       area = (float)outw * (float)outh / (float)inw / (float)inh;
+       for(y = 0; y < outh; ++y)
        {
-       case CAPTUREVIDEOFORMAT_RAWYV12:
-               // FIXME: width/height must be multiple of 2, enforce this?
-               qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo_buffer);
-               CHECKGLERROR
-               // process one line at a time, and CbCr every other line at 2 pixel intervals
-               for (y = 0;y < height;y++)
+               float iny0 =  y    / (float)outh * inh; int iny0_i = (int) floor(iny0);
+               float iny1 = (y+1) / (float)outh * inh; int iny1_i = (int) ceil(iny1);
+               for(x = 0; x < outw; ++x)
                {
-                       // 1x1 Y
-                       for (b = cls.capturevideo_buffer + (height-1-y)*width*3, out = cls.capturevideo_buffer + width*height*3 + y*width, x = 0;x < width;x++, b += 3, out++)
-                               *out = cls.capturevideo_yuvnormalizetable[0][cls.capturevideo_rgbtoyuvscaletable[0][0][b[0]] + cls.capturevideo_rgbtoyuvscaletable[0][1][b[1]] + cls.capturevideo_rgbtoyuvscaletable[0][2][b[2]]];
-                       if ((y & 1) == 0)
+                       float inx0 =  x    / (float)outw * inw; int inx0_i = (int) floor(inx0);
+                       float inx1 = (x+1) / (float)outw * inw; int inx1_i = (int) ceil(inx1);
+                       float r = 0, g = 0, b = 0, alpha = 0;
+                       int xx, yy;
+
+                       for(yy = iny0_i; yy < iny1_i; ++yy)
                        {
-                               // 2x2 Cb and Cr planes
-#if 1
-                               // low quality, no averaging
-                               for (b = cls.capturevideo_buffer + (height-2-y)*width*3, out = cls.capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
-                               {
-                                       // Cr
-                                       out[0        ] = cls.capturevideo_yuvnormalizetable[2][cls.capturevideo_rgbtoyuvscaletable[2][0][b[0]] + cls.capturevideo_rgbtoyuvscaletable[2][1][b[1]] + cls.capturevideo_rgbtoyuvscaletable[2][2][b[2]] + 128];
-                                       // Cb
-                                       out[outoffset] = cls.capturevideo_yuvnormalizetable[1][cls.capturevideo_rgbtoyuvscaletable[1][0][b[0]] + cls.capturevideo_rgbtoyuvscaletable[1][1][b[1]] + cls.capturevideo_rgbtoyuvscaletable[1][2][b[2]] + 128];
-                               }
-#else
-                               // high quality, averaging
-                               int inpitch = width*3;
-                               for (b = cls.capturevideo_buffer + (height-2-y)*width*3, out = cls.capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
+                               float ya = min(yy+1, iny1) - max(iny0, yy);
+                               for(xx = inx0_i; xx < inx1_i; ++xx)
                                {
-                                       int blockr, blockg, blockb;
-                                       blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
-                                       blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
-                                       blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
-                                       // Cr
-                                       out[0        ] = cls.capturevideo_yuvnormalizetable[2][cls.capturevideo_rgbtoyuvscaletable[2][0][blockr] + cls.capturevideo_rgbtoyuvscaletable[2][1][blockg] + cls.capturevideo_rgbtoyuvscaletable[2][2][blockb] + 128];
-                                       // Cb
-                                       out[outoffset] = cls.capturevideo_yuvnormalizetable[1][cls.capturevideo_rgbtoyuvscaletable[1][0][blockr] + cls.capturevideo_rgbtoyuvscaletable[1][1][blockg] + cls.capturevideo_rgbtoyuvscaletable[1][2][blockb] + 128];
+                                       float a = ya * (min(xx+1, inx1) - max(inx0, xx));
+                                       r += a * in[4*(xx + inw * yy)+0];
+                                       g += a * in[4*(xx + inw * yy)+1];
+                                       b += a * in[4*(xx + inw * yy)+2];
+                                       alpha += a * in[4*(xx + inw * yy)+3];
                                }
-#endif
                        }
+
+                       out[4*(x + outw * y)+0] = (unsigned char) (r * area);
+                       out[4*(x + outw * y)+1] = (unsigned char) (g * area);
+                       out[4*(x + outw * y)+2] = (unsigned char) (b * area);
+                       out[4*(x + outw * y)+3] = (unsigned char) (alpha * area);
                }
-               for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
-                       if (!FS_Write (cls.capturevideo_videofile, cls.capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
-                               return false;
-               return true;
-       case CAPTUREVIDEOFORMAT_RAWRGB:
-               qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo_buffer);
-               CHECKGLERROR
-               for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
-                       if (!FS_Write (cls.capturevideo_videofile, cls.capturevideo_buffer, width*height*3))
-                               return false;
-               return true;
-       case CAPTUREVIDEOFORMAT_JPEG:
-               qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo_buffer);
-               CHECKGLERROR
-               for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
-               {
-                       sprintf(filename, "video/dp%06d.jpg", cls.capturevideo_frame);
-                       if (!JPEG_SaveImage_preflipped (filename, width, height, cls.capturevideo_buffer))
-                               return false;
-               }
-               return true;
-       case CAPTUREVIDEOFORMAT_TARGA:
-               //return Image_WriteTGARGB_preflipped (filename, width, height, cls.capturevideo_buffer, cls.capturevideo_buffer + vid.width * vid.height * 3, );
-               memset (cls.capturevideo_buffer, 0, 18);
-               cls.capturevideo_buffer[2] = 2;         // uncompressed type
-               cls.capturevideo_buffer[12] = (width >> 0) & 0xFF;
-               cls.capturevideo_buffer[13] = (width >> 8) & 0xFF;
-               cls.capturevideo_buffer[14] = (height >> 0) & 0xFF;
-               cls.capturevideo_buffer[15] = (height >> 8) & 0xFF;
-               cls.capturevideo_buffer[16] = 24;       // pixel size
-               qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cls.capturevideo_buffer + 18);
-               CHECKGLERROR
-               for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
-               {
-                       sprintf(filename, "video/dp%06d.tga", cls.capturevideo_frame);
-                       if (!FS_WriteFile (filename, cls.capturevideo_buffer, width*height*3 + 18))
-                               return false;
-               }
-               return true;
-       default:
-               return false;
        }
 }
 
-void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate)
+void SCR_CaptureVideo_VideoFrame(int newframestepframenum)
 {
-       if (!cls.capturevideo_soundfile)
+       int x = 0, y = 0;
+       int width = cls.capturevideo.width, height = cls.capturevideo.height;
+
+       if(newframestepframenum == cls.capturevideo.framestepframe)
                return;
-       cls.capturevideo_soundrate = rate;
-       if (FS_Write (cls.capturevideo_soundfile, bufstereo16le, 4 * length) < (fs_offset_t)(4 * length))
+
+       CHECKGLERROR
+       // speed is critical here, so do saving as directly as possible
+
+       GL_ReadPixelsBGRA(x, y, vid.width, vid.height, cls.capturevideo.screenbuffer);
+
+       SCR_ScaleDownBGRA (cls.capturevideo.screenbuffer, vid.width, vid.height, cls.capturevideo.outbuffer, width, height);
+
+       cls.capturevideo.videoframes(newframestepframenum - cls.capturevideo.framestepframe);
+       cls.capturevideo.framestepframe = newframestepframenum;
+
+       if(cl_capturevideo_printfps.integer)
        {
-               Cvar_SetValueQuick(&cl_capturevideo, 0);
-               Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cls.capturevideo_frame);
-               SCR_CaptureVideo_EndVideo();
+               char buf[80];
+               double t = Sys_DoubleTime();
+               if(t > cls.capturevideo.lastfpstime + 1)
+               {
+                       double fps1 = (cls.capturevideo.frame - cls.capturevideo.lastfpsframe) / (t - cls.capturevideo.lastfpstime + 0.0000001);
+                       double fps  = (cls.capturevideo.frame                                ) / (t - cls.capturevideo.starttime   + 0.0000001);
+                       dpsnprintf(buf, sizeof(buf), "capturevideo: (%.1fs) last second %.3ffps, total %.3ffps\n", cls.capturevideo.frame / cls.capturevideo.framerate, fps1, fps);
+                       Sys_PrintToTerminal(buf);
+                       cls.capturevideo.lastfpstime = t;
+                       cls.capturevideo.lastfpsframe = cls.capturevideo.frame;
+               }
        }
 }
 
+void SCR_CaptureVideo_SoundFrame(const portable_sampleframe_t *paintbuffer, size_t length)
+{
+       cls.capturevideo.soundsampleframe += length;
+       cls.capturevideo.soundframe(paintbuffer, length);
+}
+
 void SCR_CaptureVideo(void)
 {
        int newframenum;
-       if (cl_capturevideo.integer && r_render.integer)
+       if (cl_capturevideo.integer)
        {
-               if (!cls.capturevideo_active)
+               if (!cls.capturevideo.active)
                        SCR_CaptureVideo_BeginVideo();
-               if (cls.capturevideo_framerate != cl_capturevideo_fps.value)
+               if (cls.capturevideo.framerate != cl_capturevideo_fps.value * cl_capturevideo_framestep.integer)
                {
                        Con_Printf("You can not change the video framerate while recording a video.\n");
-                       Cvar_SetValueQuick(&cl_capturevideo_fps, cls.capturevideo_framerate);
+                       Cvar_SetValueQuick(&cl_capturevideo_fps, cls.capturevideo.framerate / (double) cl_capturevideo_framestep.integer);
                }
-               if (cls.capturevideo_soundfile)
+               // for AVI saving we have to make sure that sound is saved before video
+               if (cls.capturevideo.soundrate && !cls.capturevideo.soundsampleframe)
+                       return;
+               if (cls.capturevideo.realtime)
                {
                        // preserve sound sync by duplicating frames when running slow
-                       newframenum = (Sys_DoubleTime() - cls.capturevideo_starttime) * cls.capturevideo_framerate;
+                       newframenum = (int)((realtime - cls.capturevideo.startrealtime) * cls.capturevideo.framerate);
                }
                else
-                       newframenum = cls.capturevideo_frame + 1;
+                       newframenum = cls.capturevideo.frame + 1;
                // if falling behind more than one second, stop
-               if (newframenum - cls.capturevideo_frame > (int)ceil(cls.capturevideo_framerate))
+               if (newframenum - cls.capturevideo.frame > 60 * (int)ceil(cls.capturevideo.framerate))
                {
                        Cvar_SetValueQuick(&cl_capturevideo, 0);
-                       Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cls.capturevideo_frame);
+                       Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cls.capturevideo.frame);
                        SCR_CaptureVideo_EndVideo();
                        return;
                }
                // write frames
-               if (!SCR_CaptureVideo_VideoFrame(newframenum))
+               SCR_CaptureVideo_VideoFrame(newframenum / cls.capturevideo.framestep);
+               cls.capturevideo.frame = newframenum;
+               if (cls.capturevideo.error)
                {
                        Cvar_SetValueQuick(&cl_capturevideo, 0);
-                       Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cls.capturevideo_frame);
+                       Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cls.capturevideo.frame);
                        SCR_CaptureVideo_EndVideo();
                }
        }
-       else if (cls.capturevideo_active)
+       else if (cls.capturevideo.active)
                SCR_CaptureVideo_EndVideo();
 }
 
@@ -951,7 +1340,6 @@ static void R_Envmap_f (void)
        char filename[MAX_QPATH], basename[MAX_QPATH];
        unsigned char *buffer1;
        unsigned char *buffer2;
-       unsigned char *buffer3;
 
        if (Cmd_Argc() != 3)
        {
@@ -972,70 +1360,61 @@ static void R_Envmap_f (void)
                return;
        }
 
-       envmap = true;
+       r_refdef.envmap = true;
+
+       R_UpdateVariables();
 
-       r_refdef.x = 0;
-       r_refdef.y = 0;
-       r_refdef.width = size;
-       r_refdef.height = size;
+       r_refdef.view.x = 0;
+       r_refdef.view.y = 0;
+       r_refdef.view.z = 0;
+       r_refdef.view.width = size;
+       r_refdef.view.height = size;
+       r_refdef.view.depth = 1;
+       r_refdef.view.useperspective = true;
+       r_refdef.view.isoverlay = false;
 
-       r_refdef.frustum_x = tan(90 * M_PI / 360.0);
-       r_refdef.frustum_y = tan(90 * M_PI / 360.0);
+       r_refdef.view.frustum_x = 1; // tan(45 * M_PI / 180.0);
+       r_refdef.view.frustum_y = 1; // tan(45 * M_PI / 180.0);
 
-       buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
+       buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 4);
        buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
-       buffer3 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3 + 18);
 
        for (j = 0;j < 12;j++)
        {
-               sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
-               Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], envmapinfo[j].angles[0], envmapinfo[j].angles[1], envmapinfo[j].angles[2], 1);
-               R_ClearScreen();
+               dpsnprintf(filename, sizeof(filename), "env/%s%s.tga", basename, envmapinfo[j].name);
+               Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], envmapinfo[j].angles[0], envmapinfo[j].angles[1], envmapinfo[j].angles[2], 1);
+               r_refdef.view.quality = 1;
+               r_refdef.view.clear = true;
                R_Mesh_Start();
                R_RenderView();
                R_Mesh_Finish();
-               SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, vid.height - (r_refdef.y + r_refdef.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false);
+               SCR_ScreenShot(filename, buffer1, buffer2, 0, vid.height - (r_refdef.view.y + r_refdef.view.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false, false, false);
        }
 
        Mem_Free (buffer1);
        Mem_Free (buffer2);
-       Mem_Free (buffer3);
 
-       envmap = false;
+       r_refdef.envmap = false;
 }
 
 //=============================================================================
 
-// LordHavoc: SHOWLMP stuff
-#define SHOWLMP_MAXLABELS 256
-typedef struct showlmp_s
-{
-       qboolean        isactive;
-       float           x;
-       float           y;
-       char            label[32];
-       char            pic[128];
-}
-showlmp_t;
-
-showlmp_t showlmp[SHOWLMP_MAXLABELS];
-
 void SHOWLMP_decodehide(void)
 {
        int i;
        char *lmplabel;
        lmplabel = MSG_ReadString();
-       for (i = 0;i < SHOWLMP_MAXLABELS;i++)
-               if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
+       for (i = 0;i < cl.num_showlmps;i++)
+               if (cl.showlmps[i].isactive && strcmp(cl.showlmps[i].label, lmplabel) == 0)
                {
-                       showlmp[i].isactive = false;
+                       cl.showlmps[i].isactive = false;
                        return;
                }
 }
 
 void SHOWLMP_decodeshow(void)
 {
-       int i, k;
+       int k;
        char lmplabel[256], picname[256];
        float x, y;
        strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
@@ -1050,41 +1429,37 @@ void SHOWLMP_decodeshow(void)
                x = MSG_ReadShort();
                y = MSG_ReadShort();
        }
-       k = -1;
-       for (i = 0;i < SHOWLMP_MAXLABELS;i++)
-               if (showlmp[i].isactive)
-               {
-                       if (strcmp(showlmp[i].label, lmplabel) == 0)
-                       {
-                               k = i;
-                               break; // drop out to replace it
-                       }
-               }
-               else if (k < 0) // find first empty one to replace
-                       k = i;
-       if (k < 0)
-               return; // none found to replace
-       // change existing one
-       showlmp[k].isactive = true;
-       strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
-       strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
-       showlmp[k].x = x;
-       showlmp[k].y = y;
+       if (!cl.showlmps || cl.num_showlmps >= cl.max_showlmps)
+       {
+               showlmp_t *oldshowlmps = cl.showlmps;
+               cl.max_showlmps += 16;
+               cl.showlmps = (showlmp_t *) Mem_Alloc(cls.levelmempool, cl.max_showlmps * sizeof(showlmp_t));
+               if (cl.num_showlmps)
+                       memcpy(cl.showlmps, oldshowlmps, cl.num_showlmps * sizeof(showlmp_t));
+               if (oldshowlmps)
+                       Mem_Free(oldshowlmps);
+       }
+       for (k = 0;k < cl.max_showlmps;k++)
+               if (cl.showlmps[k].isactive && !strcmp(cl.showlmps[k].label, lmplabel))
+                       break;
+       if (k == cl.max_showlmps)
+               for (k = 0;k < cl.max_showlmps;k++)
+                       if (!cl.showlmps[k].isactive)
+                               break;
+       cl.showlmps[k].isactive = true;
+       strlcpy (cl.showlmps[k].label, lmplabel, sizeof (cl.showlmps[k].label));
+       strlcpy (cl.showlmps[k].pic, picname, sizeof (cl.showlmps[k].pic));
+       cl.showlmps[k].x = x;
+       cl.showlmps[k].y = y;
+       cl.num_showlmps = max(cl.num_showlmps, k + 1);
 }
 
 void SHOWLMP_drawall(void)
 {
        int i;
-       for (i = 0;i < SHOWLMP_MAXLABELS;i++)
-               if (showlmp[i].isactive)
-                       DrawQ_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic, true), 0, 0, 1, 1, 1, 1, 0);
-}
-
-void SHOWLMP_clear(void)
-{
-       int i;
-       for (i = 0;i < SHOWLMP_MAXLABELS;i++)
-               showlmp[i].isactive = false;
+       for (i = 0;i < cl.num_showlmps;i++)
+               if (cl.showlmps[i].isactive)
+                       DrawQ_Pic(cl.showlmps[i].x, cl.showlmps[i].y, Draw_CachePic_Flags (cl.showlmps[i].pic, CACHEPICFLAG_NOTPERSISTENT), 0, 0, 1, 1, 1, 1, 0);
 }
 
 /*
@@ -1095,70 +1470,93 @@ void SHOWLMP_clear(void)
 ==============================================================================
 */
 
-qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *buffer2, unsigned char *buffer3, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg, qboolean gammacorrect)
+// buffer1: 4*w*h
+// buffer2: 3*w*h (or 4*w*h if screenshotting alpha too)
+qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *buffer2, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg, qboolean png, qboolean gammacorrect, qboolean keep_alpha)
 {
-       int     indices[3] = {0,1,2};
+       int     indices[4] = {0,1,2,3}; // BGRA
        qboolean ret;
 
-       if (!r_render.integer)
-               return false;
+       GL_ReadPixelsBGRA(x, y, width, height, buffer1);
 
-       qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer1);
-       CHECKGLERROR
-
-       if (scr_screenshot_gamma.value != 1 && gammacorrect)
+       if(gammacorrect && (scr_screenshot_gammaboost.value != 1 || WANT_SCREENSHOT_HWGAMMA))
        {
                int i;
-               double igamma = 1.0 / scr_screenshot_gamma.value;
-               unsigned char ramp[256];
-               for (i = 0;i < 256;i++)
-                       ramp[i] = (unsigned char) (pow(i * (1.0 / 255.0), igamma) * 255.0);
-               for (i = 0;i < width*height*3;i++)
-                       buffer1[i] = ramp[buffer1[i]];
+               double igamma = 1.0 / scr_screenshot_gammaboost.value;
+               unsigned short vidramp[256 * 3];
+               if(WANT_SCREENSHOT_HWGAMMA)
+               {
+                       VID_BuildGammaTables(&vidramp[0], 256);
+               }
+               else
+               {
+                       // identity gamma table
+                       BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, vidramp, 256);
+                       BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, vidramp + 256, 256);
+                       BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, vidramp + 256*2, 256);
+               }
+               if(scr_screenshot_gammaboost.value != 1)
+               {
+                       for (i = 0;i < 256 * 3;i++)
+                               vidramp[i] = (unsigned short) (0.5 + pow(vidramp[i] * (1.0 / 65535.0), igamma) * 65535.0);
+               }
+               for (i = 0;i < width*height*4;i += 4)
+               {
+                       buffer1[i] = (unsigned char) (vidramp[buffer1[i] + 512] * 255.0 / 65535.0 + 0.5); // B
+                       buffer1[i+1] = (unsigned char) (vidramp[buffer1[i+1] + 256] * 255.0 / 65535.0 + 0.5); // G
+                       buffer1[i+2] = (unsigned char) (vidramp[buffer1[i+2]] * 255.0 / 65535.0 + 0.5); // R
+                       // A
+               }
        }
 
-       Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 3, 3, indices);
-
-       if (jpeg)
-               ret = JPEG_SaveImage_preflipped (filename, width, height, buffer2);
+       if(keep_alpha && !jpeg)
+       {
+               if(!png)
+                       flipy = !flipy; // TGA: not preflipped
+               Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 4, 4, indices);
+               if (png)
+                       ret = PNG_SaveImage_preflipped (filename, width, height, true, buffer2);
+               else
+                       ret = Image_WriteTGABGRA(filename, width, height, buffer2);
+       }
        else
-               ret = Image_WriteTGARGB_preflipped (filename, width, height, buffer2, buffer3);
+       {
+               if(jpeg)
+               {
+                       indices[0] = 2;
+                       indices[2] = 0; // RGB
+               }
+               Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 3, 4, indices);
+               if (jpeg)
+                       ret = JPEG_SaveImage_preflipped (filename, width, height, buffer2);
+               else if (png)
+                       ret = PNG_SaveImage_preflipped (filename, width, height, false, buffer2);
+               else
+                       ret = Image_WriteTGABGR_preflipped (filename, width, height, buffer2);
+       }
 
        return ret;
 }
 
 //=============================================================================
 
-void R_ClearScreen(void)
+extern void R_UpdateFogColor(void);
+void R_ClearScreen(qboolean fogcolor)
 {
-       if (r_render.integer)
+       float clearcolor[4];
+       // clear to black
+       Vector4Clear(clearcolor);
+       if (fogcolor)
        {
-               // clear to black
-               if (fogenabled)
-                       qglClearColor(fogcolor[0],fogcolor[1],fogcolor[2],0);
-               else
-                       qglClearColor(0,0,0,0);
-               CHECKGLERROR
-               qglClearDepth(1);CHECKGLERROR
-               if (gl_stencil)
-               {
-                       // LordHavoc: we use a stencil centered around 128 instead of 0,
-                       // to avoid clamping interfering with strange shadow volume
-                       // drawing orders
-                       qglClearStencil(128);CHECKGLERROR
-               }
-               // clear the screen
-               GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (gl_stencil ? GL_STENCIL_BUFFER_BIT : 0));
-               // set dithering mode
-               if (gl_dither.integer)
-               {
-                       qglEnable(GL_DITHER);CHECKGLERROR
-               }
-               else
-               {
-                       qglDisable(GL_DITHER);CHECKGLERROR
-               }
+               R_UpdateFogColor();
+               VectorCopy(r_refdef.fogcolor, clearcolor);
        }
+       // clear depth is 1.0
+       // LordHavoc: we use a stencil centered around 128 instead of 0,
+       // to avoid clamping interfering with strange shadow volume
+       // drawing orders
+       // clear the screen
+       GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (vid.stencil ? GL_STENCIL_BUFFER_BIT : 0), clearcolor, 1.0f, 128);
 }
 
 qboolean CL_VM_UpdateView (void);
@@ -1167,12 +1565,18 @@ void R_Shadow_EditLights_DrawSelectedLightProperties(void);
 
 int r_stereo_side;
 
+extern void Sbar_ShowFPS(void);
 void SCR_DrawScreen (void)
 {
+       Draw_Frame();
+
        R_Mesh_Start();
 
-       if (r_timereport_active)
-               R_TimeReport("setup");
+       R_UpdateVariables();
+
+       // Quake uses clockwise winding, so these are swapped
+       r_refdef.view.cullface_front = GL_BACK;
+       r_refdef.view.cullface_back = GL_FRONT;
 
        if (cls.signon == SIGNONS)
        {
@@ -1183,19 +1587,23 @@ void SCR_DrawScreen (void)
 
                if (r_stereo_sidebyside.integer)
                {
-                       r_refdef.width = vid.width * size / 2.5;
-                       r_refdef.height = vid.height * size / 2.5 * (1 - bound(0, r_letterbox.value, 100) / 100);
-                       r_refdef.x = (vid.width - r_refdef.width * 2.5) * 0.5;
-                       r_refdef.y = (vid.height - r_refdef.height)/2;
+                       r_refdef.view.width = (int)(vid.width * size / 2.5);
+                       r_refdef.view.height = (int)(vid.height * size / 2.5 * (1 - bound(0, r_letterbox.value, 100) / 100));
+                       r_refdef.view.depth = 1;
+                       r_refdef.view.x = (int)((vid.width - r_refdef.view.width * 2.5) * 0.5);
+                       r_refdef.view.y = (int)((vid.height - r_refdef.view.height)/2);
+                       r_refdef.view.z = 0;
                        if (r_stereo_side)
-                               r_refdef.x += r_refdef.width * 1.5;
+                               r_refdef.view.x += (int)(r_refdef.view.width * 1.5);
                }
                else
                {
-                       r_refdef.width = vid.width * size;
-                       r_refdef.height = vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100);
-                       r_refdef.x = (vid.width - r_refdef.width)/2;
-                       r_refdef.y = (vid.height - r_refdef.height)/2;
+                       r_refdef.view.width = (int)(vid.width * size);
+                       r_refdef.view.height = (int)(vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100));
+                       r_refdef.view.depth = 1;
+                       r_refdef.view.x = (int)((vid.width - r_refdef.view.width)/2);
+                       r_refdef.view.y = (int)((vid.height - r_refdef.view.height)/2);
+                       r_refdef.view.z = 0;
                }
 
                // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
@@ -1204,31 +1612,33 @@ void SCR_DrawScreen (void)
                // this it simply assumes the requested fov is the vertical fov
                // for a 4x3 display, if the ratio is not 4x3 this makes the fov
                // higher/lower according to the ratio
-               r_refdef.frustum_y = tan(scr_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
-               r_refdef.frustum_x = r_refdef.frustum_y * (float)r_refdef.width / (float)r_refdef.height / vid_pixelheight.value;
+               r_refdef.view.useperspective = true;
+               r_refdef.view.frustum_y = tan(scr_fov.value * M_PI / 360.0) * (3.0/4.0) * cl.viewzoom;
+               r_refdef.view.frustum_x = r_refdef.view.frustum_y * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
 
-               r_refdef.frustum_x *= r_refdef.frustumscale_x;
-               r_refdef.frustum_y *= r_refdef.frustumscale_y;
+               r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
+               r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
 
                if(!CL_VM_UpdateView())
                        R_RenderView();
-               else
-                       SCR_DrawConsole();
 
                if (scr_zoomwindow.integer)
                {
                        float sizex = bound(10, scr_zoomwindow_viewsizex.value, 100) / 100.0;
                        float sizey = bound(10, scr_zoomwindow_viewsizey.value, 100) / 100.0;
-                       r_refdef.width = vid.width * sizex;
-                       r_refdef.height = vid.height * sizey;
-                       r_refdef.x = (vid.width - r_refdef.width)/2;
-                       r_refdef.y = 0;
+                       r_refdef.view.width = (int)(vid.width * sizex);
+                       r_refdef.view.height = (int)(vid.height * sizey);
+                       r_refdef.view.depth = 1;
+                       r_refdef.view.x = (int)((vid.width - r_refdef.view.width)/2);
+                       r_refdef.view.y = 0;
+                       r_refdef.view.z = 0;
 
-                       r_refdef.frustum_y = tan(scr_zoomwindow_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
-                       r_refdef.frustum_x = r_refdef.frustum_y * vid_pixelheight.value * (float)r_refdef.width / (float)r_refdef.height;
+                       r_refdef.view.useperspective = true;
+                       r_refdef.view.frustum_y = tan(scr_zoomwindow_fov.value * M_PI / 360.0) * (3.0/4.0) * cl.viewzoom;
+                       r_refdef.view.frustum_x = r_refdef.view.frustum_y * vid_pixelheight.value * (float)r_refdef.view.width / (float)r_refdef.view.height;
 
-                       r_refdef.frustum_x *= r_refdef.frustumscale_x;
-                       r_refdef.frustum_y *= r_refdef.frustumscale_y;
+                       r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
+                       r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
 
                        if(!CL_VM_UpdateView())
                                R_RenderView();
@@ -1237,17 +1647,51 @@ void SCR_DrawScreen (void)
 
        if (!r_stereo_sidebyside.integer)
        {
-               r_refdef.width = vid.width;
-               r_refdef.height = vid.height;
-               r_refdef.x = 0;
-               r_refdef.y = 0;
+               r_refdef.view.width = vid.width;
+               r_refdef.view.height = vid.height;
+               r_refdef.view.depth = 1;
+               r_refdef.view.x = 0;
+               r_refdef.view.y = 0;
+               r_refdef.view.z = 0;
+               r_refdef.view.useperspective = false;
        }
 
-       // draw 2D stuff
-       DrawQ_Begin();
+       if (cls.timedemo && cls.td_frames > 0 && timedemo_screenshotframelist.string && timedemo_screenshotframelist.string[0])
+       {
+               const char *t;
+               int framenum;
+               t = timedemo_screenshotframelist.string;
+               while (*t)
+               {
+                       while (*t == ' ')
+                               t++;
+                       if (!*t)
+                               break;
+                       framenum = atof(t);
+                       if (framenum == cls.td_frames)
+                               break;
+                       while (*t && *t != ' ')
+                               t++;
+               }
+               if (*t)
+               {
+                       // we need to take a screenshot of this frame...
+                       char filename[MAX_QPATH];
+                       unsigned char *buffer1;
+                       unsigned char *buffer2;
+                       dpsnprintf(filename, sizeof(filename), "timedemoscreenshots/%s%06d.tga", cls.demoname, cls.td_frames);
+                       buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 4);
+                       buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
+                       SCR_ScreenShot(filename, buffer1, buffer2, 0, 0, vid.width, vid.height, false, false, false, false, false, true, false);
+                       Mem_Free(buffer1);
+                       Mem_Free(buffer2);
+               }
+       }
 
-       //FIXME: force menu if nothing else to look at?
-       //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
+       // draw 2D stuff
+       if(!scr_con_current && !(key_consoleactive & KEY_CONSOLEACTIVE_FORCED))
+               if ((key_dest == key_game || key_dest == key_message) && !r_letterbox.value)
+                       Con_DrawNotify ();      // only draw notify in game
 
        if (cls.signon == SIGNONS)
        {
@@ -1256,108 +1700,406 @@ void SCR_DrawScreen (void)
                SCR_DrawPause ();
                if (!r_letterbox.value)
                        Sbar_Draw();
+               Sbar_ShowFPS();
                SHOWLMP_drawall();
                SCR_CheckDrawCenterString();
        }
+       SCR_DrawNetGraph ();
        MR_Draw();
        CL_DrawVideo();
        R_Shadow_EditLights_DrawSelectedLightProperties();
 
-       if(!csqc_loaded)
-               SCR_DrawConsole();
+       SCR_DrawConsole();
 
        SCR_DrawBrand();
 
-       SCR_DrawDownload();
+       SCR_DrawInfobar();
 
        if (r_timereport_active)
                R_TimeReport("2d");
 
        if (cls.signon == SIGNONS)
-               R_TimeReport_Frame();
+       {
+               R_TimeReport_EndFrame();
+               R_TimeReport_BeginFrame();
+       }
 
        DrawQ_Finish();
 
        R_DrawGamma();
 
        R_Mesh_Finish();
+}
 
-       if (r_timereport_active)
-               R_TimeReport("meshfinish");
+typedef struct loadingscreenstack_s
+{
+       struct loadingscreenstack_s *prev;
+       char msg[MAX_QPATH];
+       float absolute_loading_amount_min; // this corresponds to relative completion 0 of this item
+       float absolute_loading_amount_len; // this corresponds to relative completion 1 of this item
+       float relative_completion; // 0 .. 1
+}
+loadingscreenstack_t;
+static loadingscreenstack_t *loadingscreenstack = NULL;
+static double loadingscreentime = -1;
+static qboolean loadingscreencleared = false;
+static float loadingscreenheight = 0;
+rtexture_t *loadingscreentexture = NULL;
+static float loadingscreentexture_vertex3f[12];
+static float loadingscreentexture_texcoord2f[8];
+static int loadingscreenpic_number = 0;
+
+static void SCR_ClearLoadingScreenTexture(void)
+{
+       if(loadingscreentexture)
+               R_FreeTexture(loadingscreentexture);
+       loadingscreentexture = NULL;
 }
 
-void SCR_UpdateLoadingScreen (void)
+extern rtexturepool_t *r_main_texturepool;
+static void SCR_SetLoadingScreenTexture(void)
 {
-       float x, y;
-       cachepic_t *pic;
-       rmeshstate_t m;
-       float vertex3f[12];
-       float texcoord2f[8];
-       // don't do anything if not initialized yet
-       if (vid_hidden)
+       int w, h;
+       float loadingscreentexture_w;
+       float loadingscreentexture_h;
+
+       SCR_ClearLoadingScreenTexture();
+
+       if (vid.support.arb_texture_non_power_of_two)
+       {
+               w = vid.width; h = vid.height;
+               loadingscreentexture_w = loadingscreentexture_h = 1;
+       }
+       else
+       {
+               w = CeilPowerOf2(vid.width); h = CeilPowerOf2(vid.height);
+               loadingscreentexture_w = vid.width / (float) w;
+               loadingscreentexture_h = vid.height / (float) h;
+       }
+
+       loadingscreentexture = R_LoadTexture2D(r_main_texturepool, "loadingscreentexture", w, h, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCENEAREST | TEXF_CLAMP, -1, NULL);
+       R_Mesh_CopyToTexture(loadingscreentexture, 0, 0, 0, 0, vid.width, vid.height);
+
+       loadingscreentexture_vertex3f[2] = loadingscreentexture_vertex3f[5] = loadingscreentexture_vertex3f[8] = loadingscreentexture_vertex3f[11] = 0;
+       loadingscreentexture_vertex3f[0] = loadingscreentexture_vertex3f[9] = 0;
+       loadingscreentexture_vertex3f[1] = loadingscreentexture_vertex3f[4] = 0;
+       loadingscreentexture_vertex3f[3] = loadingscreentexture_vertex3f[6] = vid_conwidth.integer;
+       loadingscreentexture_vertex3f[7] = loadingscreentexture_vertex3f[10] = vid_conheight.integer;
+       loadingscreentexture_texcoord2f[0] = 0;loadingscreentexture_texcoord2f[1] = loadingscreentexture_h;
+       loadingscreentexture_texcoord2f[2] = loadingscreentexture_w;loadingscreentexture_texcoord2f[3] = loadingscreentexture_h;
+       loadingscreentexture_texcoord2f[4] = loadingscreentexture_w;loadingscreentexture_texcoord2f[5] = 0;
+       loadingscreentexture_texcoord2f[6] = 0;loadingscreentexture_texcoord2f[7] = 0;
+}
+
+void SCR_UpdateLoadingScreenIfShown(void)
+{
+       if(realtime == loadingscreentime)
+               SCR_UpdateLoadingScreen(loadingscreencleared);
+}
+
+void SCR_PushLoadingScreen (qboolean redraw, const char *msg, float len_in_parent)
+{
+       loadingscreenstack_t *s = (loadingscreenstack_t *) Z_Malloc(sizeof(loadingscreenstack_t));
+       s->prev = loadingscreenstack;
+       loadingscreenstack = s;
+
+       strlcpy(s->msg, msg, sizeof(s->msg));
+       s->relative_completion = 0;
+
+       if(s->prev)
+       {
+               s->absolute_loading_amount_min = s->prev->absolute_loading_amount_min + s->prev->absolute_loading_amount_len * s->prev->relative_completion;
+               s->absolute_loading_amount_len = s->prev->absolute_loading_amount_len * len_in_parent;
+               if(s->absolute_loading_amount_len > s->prev->absolute_loading_amount_min + s->prev->absolute_loading_amount_len - s->absolute_loading_amount_min)
+                       s->absolute_loading_amount_len = s->prev->absolute_loading_amount_min + s->prev->absolute_loading_amount_len - s->absolute_loading_amount_min;
+       }
+       else
+       {
+               s->absolute_loading_amount_min = 0;
+               s->absolute_loading_amount_len = 1;
+       }
+
+       if(redraw)
+               SCR_UpdateLoadingScreenIfShown();
+}
+
+void SCR_PopLoadingScreen (qboolean redraw)
+{
+       loadingscreenstack_t *s = loadingscreenstack;
+
+       if(!s)
+       {
+               Con_DPrintf("Popping a loading screen item from an empty stack!\n");
                return;
-       VID_UpdateGamma(false);
-       qglViewport(0, 0, vid.width, vid.height);
-       //qglDisable(GL_SCISSOR_TEST);
-       //qglDepthMask(1);
-       qglColorMask(1,1,1,1);
-       //qglClearColor(0,0,0,0);
-       //qglClear(GL_COLOR_BUFFER_BIT);
-       //qglCullFace(GL_FRONT);
-       //qglDisable(GL_CULL_FACE);
-       //R_ClearScreen();
+       }
+
+       loadingscreenstack = s->prev;
+       if(s->prev)
+               s->prev->relative_completion = (s->absolute_loading_amount_min + s->absolute_loading_amount_len - s->prev->absolute_loading_amount_min) / s->prev->absolute_loading_amount_len;
+       Z_Free(s);
+
+       if(redraw)
+               SCR_UpdateLoadingScreenIfShown();
+}
+
+void SCR_ClearLoadingScreen (qboolean redraw)
+{
+       while(loadingscreenstack)
+               SCR_PopLoadingScreen(redraw && !loadingscreenstack->prev);
+}
+
+static float SCR_DrawLoadingStack_r(loadingscreenstack_t *s, float y, float size)
+{
+       float x;
+       size_t len;
+       float total;
+
+       total = 0;
+#if 0
+       if(s)
+       {
+               total += SCR_DrawLoadingStack_r(s->prev, y, 8);
+               y -= total;
+               if(!s->prev || strcmp(s->msg, s->prev->msg))
+               {
+                       len = strlen(s->msg);
+                       x = (vid_conwidth.integer - DrawQ_TextWidth(s->msg, len, size, size, true, FONT_INFOBAR)) / 2;
+                       y -= size;
+                       DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 1, 0);
+                       DrawQ_String(x, y, s->msg, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
+                       total += size;
+               }
+       }
+#else
+       if(s)
+       {
+               len = strlen(s->msg);
+               x = (vid_conwidth.integer - DrawQ_TextWidth(s->msg, len, size, size, true, FONT_INFOBAR)) / 2;
+               y -= size;
+               DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 1, 0);
+               DrawQ_String(x, y, s->msg, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
+               total += size;
+       }
+#endif
+       return total;
+}
+
+static void SCR_DrawLoadingStack(void)
+{
+       float verts[12];
+       float colors[16];
+
+       loadingscreenheight = SCR_DrawLoadingStack_r(loadingscreenstack, vid_conheight.integer, scr_loadingscreen_barheight.value);
+       if(loadingscreenstack)
+       {
+               // height = 32; // sorry, using the actual one is ugly
+               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+               GL_DepthRange(0, 1);
+               GL_PolygonOffset(0, 0);
+               GL_DepthTest(false);
+               R_Mesh_ResetTextureState();
+               verts[2] = verts[5] = verts[8] = verts[11] = 0;
+               verts[0] = verts[9] = 0;
+               verts[1] = verts[4] = vid_conheight.integer - scr_loadingscreen_barheight.value;
+               verts[3] = verts[6] = vid_conwidth.integer * loadingscreenstack->absolute_loading_amount_min;
+               verts[7] = verts[10] = vid_conheight.integer;
+               
+#if _MSC_VER >= 1400
+#define sscanf sscanf_s
+#endif
+               //                                        ^^^^^^^^^^ blue component
+               //                              ^^^^^^ bottom row
+               //          ^^^^^^^^^^^^ alpha is always on
+               colors[0] = 0; colors[1] = 0; colors[2] = 0; colors[3] = 1;
+               colors[4] = 0; colors[5] = 0; colors[6] = 0; colors[7] = 1;
+               sscanf(scr_loadingscreen_barcolor.string, "%f %f %f", &colors[8], &colors[9], &colors[10]); colors[11] = 1;
+               sscanf(scr_loadingscreen_barcolor.string, "%f %f %f", &colors[12], &colors[13], &colors[14]);  colors[15] = 1;
+
+               R_Mesh_PrepareVertices_Generic_Arrays(4, verts, colors, NULL);
+               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+
+               // make sure everything is cleared, including the progress indicator
+               if(loadingscreenheight < 8)
+                       loadingscreenheight = 8;
+       }
+}
+
+static cachepic_t *loadingscreenpic;
+static float loadingscreenpic_vertex3f[12];
+static float loadingscreenpic_texcoord2f[8];
+
+static void SCR_DrawLoadingScreen_SharedSetup (qboolean clear)
+{
+       r_viewport_t viewport;
+       float x, y;
+       // release mouse grab while loading
+       if (!vid.fullscreen)
+               VID_SetMouse(false, false, false);
+//     CHECKGLERROR
+       r_refdef.draw2dstage = true;
+       R_Viewport_InitOrtho(&viewport, &identitymatrix, 0, 0, vid.width, vid.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
+       R_SetViewport(&viewport);
+       GL_ColorMask(1,1,1,1);
+       // when starting up a new video mode, make sure the screen is cleared to black
+       if (clear)
+               GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 0);
        R_Textures_Frame();
-       GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
        R_Mesh_Start();
-       R_Mesh_Matrix(&identitymatrix);
+       R_EntityMatrix(&identitymatrix);
        // draw the loading plaque
-       pic = Draw_CachePic("gfx/loading", true);
-       x = (vid_conwidth.integer - pic->width)/2;
-       y = (vid_conheight.integer - pic->height)/2;
-       GL_Color(1,1,1,1);
+       loadingscreenpic = Draw_CachePic (loadingscreenpic_number ? va("gfx/loading%d", loadingscreenpic_number+1) : "gfx/loading");
+       x = (vid_conwidth.integer - loadingscreenpic->width)/2;
+       y = (vid_conheight.integer - loadingscreenpic->height)/2;
+       loadingscreenpic_vertex3f[2] = loadingscreenpic_vertex3f[5] = loadingscreenpic_vertex3f[8] = loadingscreenpic_vertex3f[11] = 0;
+       loadingscreenpic_vertex3f[0] = loadingscreenpic_vertex3f[9] = x;
+       loadingscreenpic_vertex3f[1] = loadingscreenpic_vertex3f[4] = y;
+       loadingscreenpic_vertex3f[3] = loadingscreenpic_vertex3f[6] = x + loadingscreenpic->width;
+       loadingscreenpic_vertex3f[7] = loadingscreenpic_vertex3f[10] = y + loadingscreenpic->height;
+       loadingscreenpic_texcoord2f[0] = 0;loadingscreenpic_texcoord2f[1] = 0;
+       loadingscreenpic_texcoord2f[2] = 1;loadingscreenpic_texcoord2f[3] = 0;
+       loadingscreenpic_texcoord2f[4] = 1;loadingscreenpic_texcoord2f[5] = 1;
+       loadingscreenpic_texcoord2f[6] = 0;loadingscreenpic_texcoord2f[7] = 1;
+}
+
+static void SCR_DrawLoadingScreen (qboolean clear)
+{
+       // we only need to draw the image if it isn't already there
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       GL_DepthRange(0, 1);
+       GL_PolygonOffset(0, 0);
        GL_DepthTest(false);
-       memset(&m, 0, sizeof(m));
-       m.pointer_vertex = vertex3f;
-       m.pointer_texcoord[0] = texcoord2f;
-       m.tex[0] = R_GetTexture(pic->tex);
-       R_Mesh_State(&m);
-       vertex3f[2] = vertex3f[5] = vertex3f[8] = vertex3f[11] = 0;
-       vertex3f[0] = vertex3f[9] = x;
-       vertex3f[1] = vertex3f[4] = y;
-       vertex3f[3] = vertex3f[6] = x + pic->width;
-       vertex3f[7] = vertex3f[10] = y + pic->height;
-       texcoord2f[0] = 0;texcoord2f[1] = 0;
-       texcoord2f[2] = 1;texcoord2f[3] = 0;
-       texcoord2f[4] = 1;texcoord2f[5] = 1;
-       texcoord2f[6] = 0;texcoord2f[7] = 1;
-       R_Mesh_Draw(0, 4, 2, polygonelements);
+       R_Mesh_ResetTextureState();
+       GL_Color(1,1,1,1);
+       if(loadingscreentexture)
+       {
+               R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreentexture_vertex3f, NULL, loadingscreentexture_texcoord2f);
+               R_SetupShader_Generic(loadingscreentexture, NULL, GL_MODULATE, 1);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+       }
+       R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreenpic_vertex3f, NULL, loadingscreenpic_texcoord2f);
+       R_SetupShader_Generic(loadingscreenpic->tex, NULL, GL_MODULATE, 1);
+       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+       SCR_DrawLoadingStack();
+}
+
+static void SCR_DrawLoadingScreen_SharedFinish (qboolean clear)
+{
        R_Mesh_Finish();
        // refresh
-       VID_Finish(false);
+       VID_Finish();
+}
+
+void SCR_UpdateLoadingScreen (qboolean clear)
+{
+       keydest_t       old_key_dest;
+       int                     old_key_consoleactive;
+
+       // don't do anything if not initialized yet
+       if (vid_hidden || cls.state == ca_dedicated)
+               return;
+
+       if(!scr_loadingscreen_background.integer)
+               clear = true;
+       
+       if(loadingscreentime == realtime)
+               clear |= loadingscreencleared;
+
+       if(loadingscreentime != realtime)
+               loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer > 1 ? scr_loadingscreen_count.integer : 1);
+
+       if(clear)
+               SCR_ClearLoadingScreenTexture();
+       else if(loadingscreentime != realtime)
+               SCR_SetLoadingScreenTexture();
+
+       if(loadingscreentime != realtime)
+       {
+               loadingscreentime = realtime;
+               loadingscreenheight = 0;
+       }
+       loadingscreencleared = clear;
+
+       SCR_DrawLoadingScreen_SharedSetup(clear);
+       if (vid.stereobuffer)
+       {
+               qglDrawBuffer(GL_BACK_LEFT);
+               SCR_DrawLoadingScreen(clear);
+               qglDrawBuffer(GL_BACK_RIGHT);
+               SCR_DrawLoadingScreen(clear);
+       }
+       else
+       {
+               if (qglDrawBuffer)
+                       qglDrawBuffer(GL_BACK);
+               SCR_DrawLoadingScreen(clear);
+       }
+       SCR_DrawLoadingScreen_SharedFinish(clear);
+
+       // this goes into the event loop, and should prevent unresponsive cursor on vista
+       old_key_dest = key_dest;
+       old_key_consoleactive = key_consoleactive;
+       key_dest = key_void;
+       key_consoleactive = false;
+       Key_EventQueue_Block(); Sys_SendKeyEvents();
+       key_dest = old_key_dest;
+       key_consoleactive = old_key_consoleactive;
 }
 
+qboolean R_Stereo_ColorMasking(void)
+{
+       return r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer;
+}
+
+qboolean R_Stereo_Active(void)
+{
+       return (vid.stereobuffer || r_stereo_sidebyside.integer || R_Stereo_ColorMasking());
+}
+
+extern cvar_t cl_minfps;
+extern cvar_t cl_minfps_fade;
+extern cvar_t cl_minfps_qualitymax;
+extern cvar_t cl_minfps_qualitymin;
+extern cvar_t cl_minfps_qualitypower;
+extern cvar_t cl_minfps_qualityscale;
+static double cl_updatescreen_rendertime = 0;
+static double cl_updatescreen_quality = 1;
+extern void Sbar_ShowFPS_Update(void);
 void CL_UpdateScreen(void)
 {
+       double rendertime1;
        float conwidth, conheight;
+       float f;
+       r_viewport_t viewport;
 
-       if (vid_hidden)
-               return;
+       Sbar_ShowFPS_Update();
 
-       if (!scr_initialized || !con_initialized || vid_hidden)
+       if (!scr_initialized || !con_initialized || !scr_refresh.integer)
                return;                         // not initialized yet
 
-       // don't allow cheats in multiplayer
-       if (!cl.islocalgame && cl.worldmodel)
+       if(gamemode == GAME_NEXUIZ)
        {
-               if (r_fullbright.integer != 0)
-                       Cvar_Set ("r_fullbright", "0");
-               if (r_ambient.value != 0)
-                       Cvar_Set ("r_ambient", "0");
+               // play a bit with the palette (experimental)
+               palette_rgb_pantscolormap[15][0] = (unsigned char) (128 + 127 * sin(cl.time / exp(1.0f) + 0.0f*M_PI/3.0f));
+               palette_rgb_pantscolormap[15][1] = (unsigned char) (128 + 127 * sin(cl.time / exp(1.0f) + 2.0f*M_PI/3.0f));
+               palette_rgb_pantscolormap[15][2] = (unsigned char) (128 + 127 * sin(cl.time / exp(1.0f) + 4.0f*M_PI/3.0f));
+               palette_rgb_shirtcolormap[15][0] = (unsigned char) (128 + 127 * sin(cl.time /  M_PI  + 5.0f*M_PI/3.0f));
+               palette_rgb_shirtcolormap[15][1] = (unsigned char) (128 + 127 * sin(cl.time /  M_PI  + 3.0f*M_PI/3.0f));
+               palette_rgb_shirtcolormap[15][2] = (unsigned char) (128 + 127 * sin(cl.time /  M_PI  + 1.0f*M_PI/3.0f));
+               memcpy(palette_rgb_pantsscoreboard[15], palette_rgb_pantscolormap[15], sizeof(*palette_rgb_pantscolormap));
+               memcpy(palette_rgb_shirtscoreboard[15], palette_rgb_shirtcolormap[15], sizeof(*palette_rgb_shirtcolormap));
        }
 
-       conwidth = bound(320, vid_conwidth.value, 2048);
-       conheight = bound(200, vid_conheight.value, 1536);
+       if (vid_hidden)
+       {
+               VID_Finish();
+               return;
+       }
+
+       rendertime1 = Sys_DoubleTime();
+
+       conwidth = bound(160, vid_conwidth.value, 32768);
+       conheight = bound(90, vid_conheight.value, 24576);
        if (vid_conwidth.value != conwidth)
                Cvar_SetValue("vid_conwidth", conwidth);
        if (vid_conheight.value != conheight)
@@ -1375,16 +2117,6 @@ void CL_UpdateScreen(void)
        if (scr_fov.value > 170)
                Cvar_Set ("fov","170");
 
-       // validate r_textureunits cvar
-       if (r_textureunits.integer > gl_textureunits)
-               Cvar_SetValueQuick(&r_textureunits, gl_textureunits);
-       if (r_textureunits.integer < 1)
-               Cvar_SetValueQuick(&r_textureunits, 1);
-
-       // validate gl_combine cvar
-       if (gl_combine.integer && !gl_combine_extension)
-               Cvar_SetValueQuick(&gl_combine, 0);
-
        // intermission is always full screen
        if (cl.intermission)
                sb_lines = 0;
@@ -1398,80 +2130,137 @@ void CL_UpdateScreen(void)
                        sb_lines = 24+16+8;
        }
 
-       r_refdef.colormask[0] = 1;
-       r_refdef.colormask[1] = 1;
-       r_refdef.colormask[2] = 1;
-
-       if (r_timereport_active)
-               R_TimeReport("other");
-
-       VID_UpdateGamma(false);
+       r_refdef.view.colormask[0] = 1;
+       r_refdef.view.colormask[1] = 1;
+       r_refdef.view.colormask[2] = 1;
 
        SCR_SetUpToDrawConsole();
 
-       if (r_timereport_active)
-               R_TimeReport("start");
+       if (qglDrawBuffer)
+       {
+               CHECKGLERROR
+               qglDrawBuffer(GL_BACK);CHECKGLERROR
+               // set dithering mode
+               if (gl_dither.integer)
+               {
+                       qglEnable(GL_DITHER);CHECKGLERROR
+               }
+               else
+               {
+                       qglDisable(GL_DITHER);CHECKGLERROR
+               }
+       }
 
-       CHECKGLERROR
-       qglViewport(0, 0, vid.width, vid.height);
-       qglDisable(GL_SCISSOR_TEST);
-       qglDepthMask(1);
-       qglColorMask(1,1,1,1);
-       qglClearColor(0,0,0,0);
-       qglClear(GL_COLOR_BUFFER_BIT);
-       CHECKGLERROR
+       R_Viewport_InitOrtho(&viewport, &identitymatrix, 0, 0, vid.width, vid.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
+       R_SetViewport(&viewport);
+       GL_ScissorTest(false);
+       GL_ColorMask(1,1,1,1);
+       GL_DepthMask(true);
 
-       if (r_timereport_active)
-               R_TimeReport("clear");
+       R_ClearScreen(false);
+       r_refdef.view.clear = false;
+       r_refdef.view.isoverlay = false;
+       f = pow((float)cl_updatescreen_quality, cl_minfps_qualitypower.value) * cl_minfps_qualityscale.value;
+       r_refdef.view.quality = bound(cl_minfps_qualitymin.value, f, cl_minfps_qualitymax.value);
 
-       if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer || r_stereo_sidebyside.integer)
+       if (qglPolygonStipple)
        {
-               matrix4x4_t originalmatrix = r_refdef.viewentitymatrix;
-               r_refdef.viewentitymatrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[0][1];
-               r_refdef.viewentitymatrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[1][1];
-               r_refdef.viewentitymatrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[2][1];
+               if(scr_stipple.integer)
+               {
+                       GLubyte stipple[128];
+                       int i, s, width, parts;
+                       static int frame = 0;
+                       ++frame;
+       
+                       s = scr_stipple.integer;
+                       parts = (s & 007);
+                       width = (s & 070) >> 3;
+       
+                       qglEnable(GL_POLYGON_STIPPLE);CHECKGLERROR // 0x0B42
+                       for(i = 0; i < 128; ++i)
+                       {
+                               int line = i/4;
+                               stipple[i] = (((line >> width) + frame) & ((1 << parts) - 1)) ? 0x00 : 0xFF;
+                       }
+                       qglPolygonStipple(stipple);CHECKGLERROR
+               }
+               else
+               {
+                       qglDisable(GL_POLYGON_STIPPLE);CHECKGLERROR
+               }
+       }
+
+       if (R_Stereo_Active())
+       {
+               matrix4x4_t originalmatrix = r_refdef.view.matrix;
+               matrix4x4_t offsetmatrix;
+               Matrix4x4_CreateFromQuakeEntity(&offsetmatrix, 0, r_stereo_separation.value * 0.5f, 0, 0, r_stereo_angle.value * 0.5f, 0, 1);
+               Matrix4x4_Concat(&r_refdef.view.matrix, &originalmatrix, &offsetmatrix);
 
                if (r_stereo_sidebyside.integer)
                        r_stereo_side = 0;
 
                if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
                {
-                       r_refdef.colormask[0] = 1;
-                       r_refdef.colormask[1] = 0;
-                       r_refdef.colormask[2] = 0;
+                       r_refdef.view.colormask[0] = 1;
+                       r_refdef.view.colormask[1] = 0;
+                       r_refdef.view.colormask[2] = 0;
                }
 
+               if (vid.stereobuffer)
+                       qglDrawBuffer(GL_BACK_RIGHT);
+
                SCR_DrawScreen();
 
-               r_refdef.viewentitymatrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[0][1];
-               r_refdef.viewentitymatrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[1][1];
-               r_refdef.viewentitymatrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[2][1];
+               Matrix4x4_CreateFromQuakeEntity(&offsetmatrix, 0, r_stereo_separation.value * -0.5f, 0, 0, r_stereo_angle.value * -0.5f, 0, 1);
+               Matrix4x4_Concat(&r_refdef.view.matrix, &originalmatrix, &offsetmatrix);
 
                if (r_stereo_sidebyside.integer)
                        r_stereo_side = 1;
 
                if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
                {
-                       r_refdef.colormask[0] = 0;
-                       r_refdef.colormask[1] = r_stereo_redcyan.integer || r_stereo_redgreen.integer;
-                       r_refdef.colormask[2] = r_stereo_redcyan.integer || r_stereo_redblue.integer;
+                       r_refdef.view.colormask[0] = 0;
+                       r_refdef.view.colormask[1] = r_stereo_redcyan.integer || r_stereo_redgreen.integer;
+                       r_refdef.view.colormask[2] = r_stereo_redcyan.integer || r_stereo_redblue.integer;
                }
 
+               if (vid.stereobuffer)
+                       qglDrawBuffer(GL_BACK_LEFT);
+
                SCR_DrawScreen();
 
-               r_refdef.viewentitymatrix = originalmatrix;
+               r_refdef.view.matrix = originalmatrix;
        }
        else
                SCR_DrawScreen();
 
        SCR_CaptureVideo();
 
-       VID_Finish(true);
-       if (r_timereport_active)
-               R_TimeReport("finish");
+       if (qglFlush)
+               qglFlush(); // FIXME: should we really be using qglFlush here?
+
+       // quality adjustment according to render time
+       cl_updatescreen_rendertime += ((Sys_DoubleTime() - rendertime1) - cl_updatescreen_rendertime) * bound(0, cl_minfps_fade.value, 1);
+       if (cl_minfps.value > 0 && cl_updatescreen_rendertime > 0 && !cls.timedemo && (!cls.capturevideo.active || !cls.capturevideo.realtime))
+               cl_updatescreen_quality = 1 / (cl_updatescreen_rendertime * cl_minfps.value);
+       else
+               cl_updatescreen_quality = 1;
+
+       if (!vid_activewindow)
+               VID_SetMouse(false, false, false);
+       else if (key_consoleactive)
+               VID_SetMouse(vid.fullscreen, false, false);
+       else if (key_dest == key_menu_grabbed)
+               VID_SetMouse(true, vid_mouse.integer && !in_client_mouse, true);
+       else if (key_dest == key_menu)
+               VID_SetMouse(vid.fullscreen, vid_mouse.integer && !in_client_mouse, true);
+       else
+               VID_SetMouse(vid.fullscreen, vid_mouse.integer && !cl.csqc_wantsmousemove && cl_prydoncursor.integer <= 0 && (!cls.demoplayback || cl_demo_mousegrab.integer), true);
+
+       VID_Finish();
 }
 
 void CL_Screen_NewMap(void)
 {
-       SHOWLMP_clear();
 }