]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_screen.c
cleaned up PF_fopen filename rejection, now rejects more unsafe names, including...
[divverent/darkplaces.git] / cl_screen.c
1
2 #include "quakedef.h"
3 #include "cl_video.h"
4 #include "jpeg.h"
5 #include "cl_collision.h"
6
7 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
8 cvar_t scr_fov = {CVAR_SAVE, "fov","90"};       // 1 - 170
9 cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900"}; // LordHavoc: quake used 300
10 cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1"};
11 cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "0.2"};
12 cvar_t scr_conforcewhiledisconnected = {CVAR_SAVE, "scr_conforcewhiledisconnected", "1"};
13 cvar_t scr_centertime = {0, "scr_centertime","2"};
14 cvar_t scr_showram = {CVAR_SAVE, "showram","1"};
15 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
16 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
17 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
18 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640"};
19 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480"};
20 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","0"};
21 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9"};
22 cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp"};
23 cvar_t cl_avidemo = {0, "cl_avidemo", "0"};
24
25 int jpeg_supported = false;
26
27 qboolean        scr_initialized;                // ready to draw
28
29 float           scr_con_current;
30 float           scr_conlines;           // lines of console to display
31
32 int                     clearconsole;
33 int                     clearnotify;
34
35 qboolean        scr_drawloading = false;
36
37 void DrawCrosshair(int num);
38 static void SCR_ScreenShot_f (void);
39 static void R_Envmap_f (void);
40
41 // backend
42 void R_ClearScreen(void);
43
44 /*
45 ===============================================================================
46
47 CENTER PRINTING
48
49 ===============================================================================
50 */
51
52 char            scr_centerstring[1024];
53 float           scr_centertime_start;   // for slow victory printing
54 float           scr_centertime_off;
55 int                     scr_center_lines;
56 int                     scr_erase_lines;
57 int                     scr_erase_center;
58
59 /*
60 ==============
61 SCR_CenterPrint
62
63 Called for important messages that should stay in the center of the screen
64 for a few moments
65 ==============
66 */
67 void SCR_CenterPrint(char *str)
68 {
69         strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
70         scr_centertime_off = scr_centertime.value;
71         scr_centertime_start = cl.time;
72
73 // count the number of lines for centering
74         scr_center_lines = 1;
75         while (*str)
76         {
77                 if (*str == '\n')
78                         scr_center_lines++;
79                 str++;
80         }
81 }
82
83
84 void SCR_DrawCenterString (void)
85 {
86         char    *start;
87         int             l;
88         int             x, y;
89         int             remaining;
90
91 // the finale prints the characters one at a time
92         if (cl.intermission)
93                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
94         else
95                 remaining = 9999;
96
97         scr_erase_center = 0;
98         start = scr_centerstring;
99
100         if (scr_center_lines <= 4)
101                 y = vid.conheight*0.35;
102         else
103                 y = 48;
104
105         do
106         {
107         // scan the width of the line
108                 for (l=0 ; l<40 ; l++)
109                         if (start[l] == '\n' || !start[l])
110                                 break;
111                 x = (vid.conwidth - l*8)/2;
112                 if (l > 0)
113                 {
114                         if (remaining < l)
115                                 l = remaining;
116                         DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
117                         remaining -= l;
118                         if (remaining <= 0)
119                                 return;
120                 }
121
122                 y += 8;
123
124                 while (*start && *start != '\n')
125                         start++;
126
127                 if (!*start)
128                         break;
129                 start++;                // skip the \n
130         } while (1);
131 }
132
133 void SCR_CheckDrawCenterString (void)
134 {
135         if (scr_center_lines > scr_erase_lines)
136                 scr_erase_lines = scr_center_lines;
137
138         scr_centertime_off -= host_frametime;
139
140         // don't draw if this is a normal stats-screen intermission,
141         // only if it is not an intermission, or a finale intermission
142         if (cl.intermission == 1)
143                 return;
144         if (scr_centertime_off <= 0 && !cl.intermission)
145                 return;
146         if (key_dest != key_game)
147                 return;
148
149         SCR_DrawCenterString ();
150 }
151
152 /*
153 ==============
154 SCR_DrawTurtle
155 ==============
156 */
157 void SCR_DrawTurtle (void)
158 {
159         static int      count;
160
161         if (cls.state != ca_connected)
162                 return;
163
164         if (!scr_showturtle.integer)
165                 return;
166
167         if (host_frametime < 0.1)
168         {
169                 count = 0;
170                 return;
171         }
172
173         count++;
174         if (count < 3)
175                 return;
176
177         DrawQ_Pic (0, 0, "gfx/turtle.lmp", 0, 0, 1, 1, 1, 1, 0);
178 }
179
180 /*
181 ==============
182 SCR_DrawNet
183 ==============
184 */
185 void SCR_DrawNet (void)
186 {
187         if (cls.state != ca_connected)
188                 return;
189         if (realtime - cl.last_received_message < 0.3)
190                 return;
191         if (cls.demoplayback)
192                 return;
193
194         DrawQ_Pic (64, 0, "gfx/net.lmp", 0, 0, 1, 1, 1, 1, 0);
195 }
196
197 /*
198 ==============
199 DrawPause
200 ==============
201 */
202 void SCR_DrawPause (void)
203 {
204         cachepic_t      *pic;
205
206         if (cls.state != ca_connected)
207                 return;
208
209         if (!scr_showpause.integer)             // turn off for screenshots
210                 return;
211
212         if (!cl.paused)
213                 return;
214
215         pic = Draw_CachePic ("gfx/pause.lmp");
216         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
217 }
218
219
220
221 /*
222 ==============
223 SCR_DrawLoading
224 ==============
225 */
226 void SCR_DrawLoading (void)
227 {
228         cachepic_t      *pic;
229
230         pic = Draw_CachePic ("gfx/loading.lmp");
231         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/loading.lmp", 0, 0, 1, 1, 1, 1, 0);
232 }
233
234
235
236 //=============================================================================
237
238
239 /*
240 ==================
241 SCR_SetUpToDrawConsole
242 ==================
243 */
244 void SCR_SetUpToDrawConsole (void)
245 {
246         Con_CheckResize ();
247
248         if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
249                 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
250         else
251                 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
252
253 // decide on the height of the console
254         if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
255                 scr_conlines = vid.conheight; // full screen
256         else if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
257                 scr_conlines = vid.conheight/2; // half screen
258         else
259                 scr_conlines = 0;                               // none visible
260
261         if (scr_conspeed.value)
262         {
263                 if (scr_conlines < scr_con_current)
264                 {
265                         scr_con_current -= scr_conspeed.value*host_realframetime;
266                         if (scr_conlines > scr_con_current)
267                                 scr_con_current = scr_conlines;
268
269                 }
270                 else if (scr_conlines > scr_con_current)
271                 {
272                         scr_con_current += scr_conspeed.value*host_realframetime;
273                         if (scr_conlines < scr_con_current)
274                                 scr_con_current = scr_conlines;
275                 }
276         }
277         else
278                 scr_con_current = scr_conlines;
279 }
280
281 /*
282 ==================
283 SCR_DrawConsole
284 ==================
285 */
286 void SCR_DrawConsole (void)
287 {
288         if (scr_con_current)
289         {
290                 Con_DrawConsole (scr_con_current);
291                 clearconsole = 0;
292         }
293         else
294         {
295                 if (key_dest == key_game || key_dest == key_message)
296                         Con_DrawNotify ();      // only draw notify in game
297         }
298 }
299
300 /*
301 ===============
302 SCR_BeginLoadingPlaque
303
304 ================
305 */
306 void SCR_BeginLoadingPlaque (void)
307 {
308         if (scr_drawloading)
309                 return;
310
311         S_StopAllSounds (true);
312
313         scr_drawloading = true;
314         CL_UpdateScreen ();
315         scr_drawloading = true;
316         CL_UpdateScreen ();
317 }
318
319 //=============================================================================
320
321 char r_speeds_string[1024];
322 int speedstringcount, r_timereport_active;
323 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
324
325 void R_TimeReport(char *desc)
326 {
327         char tempbuf[256];
328         int length;
329         int t;
330
331         if (!r_timereport_active)
332                 return;
333
334         r_timereport_temp = r_timereport_current;
335         r_timereport_current = Sys_DoubleTime();
336         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
337
338         sprintf(tempbuf, "%8i %s", t, desc);
339         length = strlen(tempbuf);
340         while (length < 20)
341                 tempbuf[length++] = ' ';
342         tempbuf[length] = 0;
343         if (speedstringcount + length > (vid.conwidth / 8))
344         {
345                 strcat(r_speeds_string, "\n");
346                 speedstringcount = 0;
347         }
348         // skip the space at the beginning if it's the first on the line
349         if (speedstringcount == 0)
350         {
351                 strcat(r_speeds_string, tempbuf + 1);
352                 speedstringcount = length - 1;
353         }
354         else
355         {
356                 strcat(r_speeds_string, tempbuf);
357                 speedstringcount += length;
358         }
359 }
360
361 extern int c_rt_lights, c_rt_clears, c_rt_scissored;
362 extern int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
363 extern int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
364 void R_TimeReport_Start(void)
365 {
366         r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
367         r_speeds_string[0] = 0;
368         if (r_timereport_active)
369         {
370                 speedstringcount = 0;
371                 sprintf(r_speeds_string,
372                         "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n"
373                         "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n"
374                         "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
375                         "%6i modeltris%6i meshs%6i meshtris\n",
376                         r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2],
377                         c_faces, c_nodes, c_leafs, c_light_polys,
378                         c_models, c_bmodels, c_sprites, c_particles, c_dlights,
379                         c_alias_polys, c_meshs, c_meshelements / 3);
380
381                 sprintf(r_speeds_string + strlen(r_speeds_string),
382                         "realtime lighting:%4i lights%4i clears%4i scissored\n"
383                         "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n"
384                         "precomputed: %6i shadowmeshes%6i shadowtris\n",
385                         c_rt_lights, c_rt_clears, c_rt_scissored,
386                         c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris,
387                         c_rtcached_shadowmeshes, c_rtcached_shadowtris);
388
389                 c_alias_polys = 0;
390                 c_light_polys = 0;
391                 c_faces = 0;
392                 c_nodes = 0;
393                 c_leafs = 0;
394                 c_models = 0;
395                 c_bmodels = 0;
396                 c_sprites = 0;
397                 c_particles = 0;
398                 c_meshs = 0;
399                 c_meshelements = 0;
400
401                 r_timereport_start = Sys_DoubleTime();
402         }
403 }
404
405 void R_TimeReport_End(void)
406 {
407         r_timereport_current = r_timereport_start;
408         R_TimeReport("total");
409
410         if (r_timereport_active)
411         {
412                 int i, j, lines, y;
413                 lines = 1;
414                 for (i = 0;r_speeds_string[i];i++)
415                         if (r_speeds_string[i] == '\n')
416                                 lines++;
417                 y = vid.conheight - sb_lines - lines * 8;
418                 i = j = 0;
419                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
420                 while (r_speeds_string[i])
421                 {
422                         j = i;
423                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
424                                 i++;
425                         if (i - j > 0)
426                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
427                         if (r_speeds_string[i] == '\n')
428                                 i++;
429                         y += 8;
430                 }
431         }
432 }
433
434 /*
435 =================
436 SCR_SizeUp_f
437
438 Keybinding command
439 =================
440 */
441 void SCR_SizeUp_f (void)
442 {
443         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
444 }
445
446
447 /*
448 =================
449 SCR_SizeDown_f
450
451 Keybinding command
452 =================
453 */
454 void SCR_SizeDown_f (void)
455 {
456         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
457 }
458
459 void CL_Screen_Init(void)
460 {
461         Cvar_RegisterVariable (&scr_fov);
462         Cvar_RegisterVariable (&scr_viewsize);
463         Cvar_RegisterVariable (&scr_conspeed);
464         Cvar_RegisterVariable (&scr_conalpha);
465         Cvar_RegisterVariable (&scr_conbrightness);
466         Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
467         Cvar_RegisterVariable (&scr_showram);
468         Cvar_RegisterVariable (&scr_showturtle);
469         Cvar_RegisterVariable (&scr_showpause);
470         Cvar_RegisterVariable (&scr_centertime);
471         Cvar_RegisterVariable (&scr_printspeed);
472         Cvar_RegisterVariable (&vid_conwidth);
473         Cvar_RegisterVariable (&vid_conheight);
474         Cvar_RegisterVariable (&scr_screenshot_jpeg);
475         Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
476         Cvar_RegisterVariable (&cl_avidemo);
477
478         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
479         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
480         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
481         Cmd_AddCommand ("envmap", R_Envmap_f);
482
483         scr_initialized = true;
484 }
485
486 void DrawQ_Clear(void)
487 {
488         r_refdef.drawqueuesize = 0;
489 }
490
491 static int picelements[6] = {0, 1, 2, 0, 2, 3};
492 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
493 {
494         DrawQ_SuperPic(x,y,picname,width,height,0,0,red,green,blue,alpha,1,0,red,green,blue,alpha,0,1,red,green,blue,alpha,1,1,red,green,blue,alpha,flags);
495 }
496
497 void DrawQ_String(float x, float y, const char *string, int maxlen, float scalex, float scaley, float red, float green, float blue, float alpha, int flags)
498 {
499         int size, len;
500         drawqueue_t *dq;
501         char *out;
502         if (alpha < (1.0f / 255.0f))
503                 return;
504         if (maxlen < 1)
505                 len = strlen(string);
506         else
507                 for (len = 0;len < maxlen && string[len];len++);
508         for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
509         for (;len > 0 && string[len - 1] == ' ';len--);
510         if (len < 1)
511                 return;
512         if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * maxlen) || y < (-scaley))
513                 return;
514         size = sizeof(*dq) + ((len + 1 + 3) & ~3);
515         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
516                 return;
517         red = bound(0, red, 1);
518         green = bound(0, green, 1);
519         blue = bound(0, blue, 1);
520         alpha = bound(0, alpha, 1);
521         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
522         dq->size = size;
523         dq->command = DRAWQUEUE_STRING;
524         dq->flags = flags;
525         dq->color = ((unsigned int) (red * 255.0f) << 24) | ((unsigned int) (green * 255.0f) << 16) | ((unsigned int) (blue * 255.0f) << 8) | ((unsigned int) (alpha * 255.0f));
526         dq->x = x;
527         dq->y = y;
528         dq->scalex = scalex;
529         dq->scaley = scaley;
530         out = (char *)(dq + 1);
531         memcpy(out, string, len);
532         out[len] = 0;
533         r_refdef.drawqueuesize += dq->size;
534 }
535
536 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
537 {
538         DrawQ_SuperPic(x,y,NULL,w,h,0,0,red,green,blue,alpha,1,0,red,green,blue,alpha,0,1,red,green,blue,alpha,1,1,red,green,blue,alpha,flags);
539 }
540
541 void DrawQ_SuperPic(float x, float y, char *picname, float width, float height, float s1, float t1, float r1, float g1, float b1, float a1, float s2, float t2, float r2, float g2, float b2, float a2, float s3, float t3, float r3, float g3, float b3, float a3, float s4, float t4, float r4, float g4, float b4, float a4, int flags)
542 {
543         float floats[36];
544         cachepic_t *pic;
545         drawqueuemesh_t mesh;
546         memset(&mesh, 0, sizeof(mesh));
547         if (picname && picname[0])
548         {
549                 pic = Draw_CachePic(picname);
550                 if (width == 0)
551                         width = pic->width;
552                 if (height == 0)
553                         height = pic->height;
554                 mesh.texture = pic->tex;
555         }
556         mesh.num_triangles = 2;
557         mesh.num_vertices = 4;
558         mesh.data_element3i = picelements;
559         mesh.data_vertex3f = floats;
560         mesh.data_texcoord2f = floats + 12;
561         mesh.data_color4f = floats + 20;
562         memset(floats, 0, sizeof(floats));
563         mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
564         mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
565         mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
566         mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
567         mesh.data_texcoord2f[0] = s1;mesh.data_texcoord2f[1] = t1;mesh.data_color4f[ 0] = r1;mesh.data_color4f[ 1] = g1;mesh.data_color4f[ 2] = b1;mesh.data_color4f[ 3] = a1;
568         mesh.data_texcoord2f[2] = s2;mesh.data_texcoord2f[3] = t2;mesh.data_color4f[ 4] = r2;mesh.data_color4f[ 5] = g2;mesh.data_color4f[ 6] = b2;mesh.data_color4f[ 7] = a2;
569         mesh.data_texcoord2f[4] = s4;mesh.data_texcoord2f[5] = t4;mesh.data_color4f[ 8] = r4;mesh.data_color4f[ 9] = g4;mesh.data_color4f[10] = b4;mesh.data_color4f[11] = a4;
570         mesh.data_texcoord2f[6] = s3;mesh.data_texcoord2f[7] = t3;mesh.data_color4f[12] = r3;mesh.data_color4f[13] = g3;mesh.data_color4f[14] = b3;mesh.data_color4f[15] = a3;
571         DrawQ_Mesh (&mesh, flags);
572 }
573
574 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
575 {
576         int size;
577         void *p;
578         drawqueue_t *dq;
579         drawqueuemesh_t *m;
580         size = sizeof(*dq);
581         size += sizeof(drawqueuemesh_t);
582         size += sizeof(int[3]) * mesh->num_triangles;
583         size += sizeof(float[3]) * mesh->num_vertices;
584         size += sizeof(float[2]) * mesh->num_vertices;
585         size += sizeof(float[4]) * mesh->num_vertices;
586         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
587                 return;
588         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
589         dq->size = size;
590         dq->command = DRAWQUEUE_MESH;
591         dq->flags = flags;
592         dq->color = 0;
593         dq->x = 0;
594         dq->y = 0;
595         dq->scalex = 0;
596         dq->scaley = 0;
597         p = (void *)(dq + 1);
598         m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
599         m->num_triangles = mesh->num_triangles;
600         m->num_vertices = mesh->num_vertices;
601         m->texture = mesh->texture;
602         m->data_element3i  = p;memcpy(m->data_element3i , mesh->data_element3i , m->num_triangles * sizeof(int[3]));p = (qbyte*)p + m->num_triangles * sizeof(int[3]);
603         m->data_vertex3f   = p;memcpy(m->data_vertex3f  , mesh->data_vertex3f  , m->num_vertices * sizeof(float[3]));p = (qbyte*)p + m->num_vertices * sizeof(float[3]);
604         m->data_texcoord2f = p;memcpy(m->data_texcoord2f, mesh->data_texcoord2f, m->num_vertices * sizeof(float[2]));p = (qbyte*)p + m->num_vertices * sizeof(float[2]);
605         m->data_color4f    = p;memcpy(m->data_color4f   , mesh->data_color4f   , m->num_vertices * sizeof(float[4]));p = (qbyte*)p + m->num_vertices * sizeof(float[4]);
606         r_refdef.drawqueuesize += dq->size;
607 }
608
609 void DrawQ_SetClipArea(float x, float y, float width, float height)
610 {
611         drawqueue_t * dq;
612         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
613         {
614                 Con_DPrint("DrawQueue full !\n");
615                 return;
616         }
617         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
618         dq->size = sizeof(*dq);
619         dq->command = DRAWQUEUE_SETCLIP;
620         dq->x = x;
621         dq->y = y;
622         dq->scalex = width;
623         dq->scaley = height;
624         dq->flags = 0;
625         dq->color = 0;
626
627         r_refdef.drawqueuesize += dq->size;
628 }
629
630 void DrawQ_ResetClipArea(void)
631 {
632         drawqueue_t *dq;
633         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
634         {
635                 Con_DPrint("DrawQueue full !\n");
636                 return;
637         }
638         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
639         dq->size = sizeof(*dq);
640         dq->command = DRAWQUEUE_RESETCLIP;
641         dq->x = 0;
642         dq->y = 0;
643         dq->scalex = 0;
644         dq->scaley = 0;
645         dq->flags = 0;
646         dq->color = 0;
647
648         r_refdef.drawqueuesize += dq->size;
649 }
650
651 /*
652 ==================
653 SCR_ScreenShot_f
654 ==================
655 */
656 void SCR_ScreenShot_f (void)
657 {
658         int shotnumber;
659         char base[MAX_QPATH];
660         char filename[MAX_QPATH];
661         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
662
663         sprintf (base, "screenshots/%s", scr_screenshot_name.string);
664         
665         // find a file name to save it to
666         for (shotnumber=0;shotnumber < 1000000;shotnumber++)
667                 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
668                         break;
669         if (shotnumber >= 1000000)
670         {
671                 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
672                 return;
673         }
674
675         if (jpeg)
676                 sprintf(filename, "%s%06d.jpg", base, shotnumber);
677         else
678                 sprintf(filename, "%s%06d.tga", base, shotnumber);
679
680         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
681                 Con_Printf("Wrote %s\n", filename);
682         else
683                 Con_Printf("unable to write %s\n", filename);
684         shotnumber++;
685 }
686
687 static int cl_avidemo_frame = 0;
688
689 void SCR_CaptureAVIDemo(void)
690 {
691         char filename[32];
692         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
693
694         if (jpeg)
695                 sprintf(filename, "video/dp%06d.jpg", cl_avidemo_frame);
696         else
697                 sprintf(filename, "video/dp%06d.tga", cl_avidemo_frame);
698
699         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
700                 cl_avidemo_frame++;
701         else
702         {
703                 Cvar_SetValueQuick(&cl_avidemo, 0);
704                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
705                 cl_avidemo_frame = 0;
706         }
707 }
708
709 /*
710 ===============
711 R_Envmap_f
712
713 Grab six views for environment mapping tests
714 ===============
715 */
716 struct
717 {
718         float angles[3];
719         char *name;
720         qboolean flipx, flipy, flipdiagonaly;
721 }
722 envmapinfo[12] =
723 {
724         {{  0,   0, 0}, "rt",  true, false, false},
725         {{  0,  90, 0}, "ft",  true, false, false},
726         {{  0, 180, 0}, "lf",  true, false, false},
727         {{  0, 270, 0}, "bk",  true, false, false},
728         {{-90, 180, 0}, "up", false,  true, false},
729         {{ 90, 180, 0}, "dn", false,  true, false},
730
731         {{  0,   0, 0}, "px",  true,  true,  true},
732         {{  0,  90, 0}, "py", false,  true, false},
733         {{  0, 180, 0}, "nx", false, false,  true},
734         {{  0, 270, 0}, "ny",  true, false, false},
735         {{-90, 180, 0}, "pz", false, false,  true},
736         {{ 90, 180, 0}, "nz", false, false,  true}
737 };
738
739 static void R_Envmap_f (void)
740 {
741         int j, size;
742         char filename[256], basename[256];
743
744         if (Cmd_Argc() != 3)
745         {
746                 Con_Print("envmap <basename> <size>: save out 6 cubic environment map images, usable with loadsky, note that size must one of 128, 256, 512, or 1024 and can't be bigger than your current resolution\n");
747                 return;
748         }
749
750         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
751         size = atoi(Cmd_Argv(2));
752         if (size != 128 && size != 256 && size != 512 && size != 1024)
753         {
754                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
755                 return;
756         }
757         if (size > vid.realwidth || size > vid.realheight)
758         {
759                 Con_Print("envmap: your resolution is not big enough to render that size\n");
760                 return;
761         }
762
763         envmap = true;
764
765         r_refdef.x = 0;
766         r_refdef.y = 0;
767         r_refdef.width = size;
768         r_refdef.height = size;
769
770         r_refdef.fov_x = 90;
771         r_refdef.fov_y = 90;
772
773         for (j = 0;j < 12;j++)
774         {
775                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
776                 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);
777                 R_ClearScreen();
778                 R_Mesh_Start();
779                 R_RenderView();
780                 R_Mesh_Finish();
781                 SCR_ScreenShot(filename, vid.realx, vid.realy + vid.realheight - (r_refdef.y + r_refdef.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false);
782         }
783
784         envmap = false;
785 }
786
787 //=============================================================================
788
789 // LordHavoc: SHOWLMP stuff
790 #define SHOWLMP_MAXLABELS 256
791 typedef struct showlmp_s
792 {
793         qboolean        isactive;
794         float           x;
795         float           y;
796         char            label[32];
797         char            pic[128];
798 }
799 showlmp_t;
800
801 showlmp_t showlmp[SHOWLMP_MAXLABELS];
802
803 void SHOWLMP_decodehide(void)
804 {
805         int i;
806         qbyte *lmplabel;
807         lmplabel = MSG_ReadString();
808         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
809                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
810                 {
811                         showlmp[i].isactive = false;
812                         return;
813                 }
814 }
815
816 void SHOWLMP_decodeshow(void)
817 {
818         int i, k;
819         qbyte lmplabel[256], picname[256];
820         float x, y;
821         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
822         strlcpy (picname, MSG_ReadString(), sizeof (picname));
823         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
824         {
825                 x = MSG_ReadByte();
826                 y = MSG_ReadByte();
827         }
828         else
829         {
830                 x = MSG_ReadShort();
831                 y = MSG_ReadShort();
832         }
833         k = -1;
834         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
835                 if (showlmp[i].isactive)
836                 {
837                         if (strcmp(showlmp[i].label, lmplabel) == 0)
838                         {
839                                 k = i;
840                                 break; // drop out to replace it
841                         }
842                 }
843                 else if (k < 0) // find first empty one to replace
844                         k = i;
845         if (k < 0)
846                 return; // none found to replace
847         // change existing one
848         showlmp[k].isactive = true;
849         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
850         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
851         showlmp[k].x = x;
852         showlmp[k].y = y;
853 }
854
855 void SHOWLMP_drawall(void)
856 {
857         int i;
858         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
859                 if (showlmp[i].isactive)
860                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
861 }
862
863 void SHOWLMP_clear(void)
864 {
865         int i;
866         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
867                 showlmp[i].isactive = false;
868 }
869
870 void CL_SetupScreenSize(void)
871 {
872         float conwidth, conheight;
873
874         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
875
876         VID_UpdateGamma(false);
877
878         conwidth = bound(320, vid_conwidth.value, 2048);
879         conheight = bound(200, vid_conheight.value, 1536);
880         if (vid_conwidth.value != conwidth)
881                 Cvar_SetValue("vid_conwidth", conwidth);
882         if (vid_conheight.value != conheight)
883                 Cvar_SetValue("vid_conheight", conheight);
884
885         vid.conwidth = vid_conwidth.integer;
886         vid.conheight = vid_conheight.integer;
887
888 /*      if (vid.realheight > 240)
889         {
890                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
891                 vid.conheight = bound(240, vid.conheight, vid.realheight);
892         }
893         else
894                 vid.conheight = 240;*/
895
896         SCR_SetUpToDrawConsole();
897 }
898
899 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
900 void CL_UpdateScreen(void)
901 {
902         if (!scr_initialized || !con_initialized || vid_hidden)
903                 return;                         // not initialized yet
904
905         if (cl_avidemo.integer)
906                 SCR_CaptureAVIDemo();
907         else
908                 cl_avidemo_frame = 0;
909
910         if (cls.signon == SIGNONS)
911                 R_TimeReport("other");
912
913         CL_SetupScreenSize();
914
915         DrawQ_Clear();
916
917         if (cls.signon == SIGNONS)
918                 R_TimeReport("setup");
919
920         //FIXME: force menu if nothing else to look at?
921         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
922
923         if (scr_drawloading)
924         {
925                 scr_drawloading = false;
926                 SCR_DrawLoading();
927         }
928         else
929         {
930                 if (cls.signon == SIGNONS)
931                 {
932                         SCR_DrawNet ();
933                         SCR_DrawTurtle ();
934                         SCR_DrawPause ();
935                         Sbar_Draw();
936                         SHOWLMP_drawall();
937                         SCR_CheckDrawCenterString();
938                 }
939                 MR_Draw();
940                 UI_Callback_Draw();
941                 CL_DrawVideo();
942                 //ui_draw();
943                 if (cls.signon == SIGNONS)
944                 {
945                         R_TimeReport("2d");
946                         R_TimeReport_End();
947                         R_TimeReport_Start();
948                 }
949                 R_Shadow_EditLights_DrawSelectedLightProperties();
950         }
951         SCR_DrawConsole();
952
953         SCR_UpdateScreen();
954 }
955
956 void CL_Screen_NewMap(void)
957 {
958         SHOWLMP_clear();
959 }