+ con_lineinfo_t *li = &CON_LINES(lineno);
+ if(li->height == -1)
+ {
+ float width = vid_conwidth.value;
+ con_text_info_t ti;
+ ti.fontsize = con_textsize.value;
+ ti.font = FONT_CONSOLE;
+ li->height = COM_Wordwrap(li->start, li->len, 0, width, Con_WordWidthFunc, &ti, Con_CountLineFunc, NULL);
+ }
+ return li->height;
+}
+
+/*
+================
+Con_DrawConsoleLine
+
+Draws a line of the console; returns its height in lines.
+If alpha is 0, the line is not drawn, but still wrapped and its height
+returned.
+================
+*/
+static int Con_DrawConsoleLine(int mask_must, int mask_mustnot, float y, int lineno, float ymin, float ymax)
+{
+ float width = vid_conwidth.value;
+ con_text_info_t ti;
+ con_lineinfo_t *li = &CON_LINES(lineno);
+
+ if((li->mask & mask_must) != mask_must)
+ return 0;
+ if((li->mask & mask_mustnot) != 0)
+ return 0;
+
+ ti.continuationString = "";
+ ti.alignment = 0;
+ ti.fontsize = con_textsize.value;
+ ti.font = FONT_CONSOLE;
+ ti.x = 0;
+ ti.y = y - (Con_LineHeight(lineno) - 1) * ti.fontsize;
+ ti.ymin = ymin;
+ ti.ymax = ymax;
+ ti.width = width;
+
+ return COM_Wordwrap(li->start, li->len, 0, width, Con_WordWidthFunc, &ti, Con_DisplayLineFunc, &ti);
+}
+
+/*
+================
+Con_LastVisibleLine
+
+Calculates the last visible line index and how much to show of it based on
+con_backscroll.
+================
+*/
+static void Con_LastVisibleLine(int mask_must, int mask_mustnot, int *last, int *limitlast)
+{
+ int lines_seen = 0;
+ int i;
+
+ if(con_backscroll < 0)
+ con_backscroll = 0;
+
+ *last = 0;
+
+ // now count until we saw con_backscroll actual lines
+ for(i = CON_LINES_COUNT - 1; i >= 0; --i)
+ if((CON_LINES(i).mask & mask_must) == mask_must)
+ if((CON_LINES(i).mask & mask_mustnot) == 0)
+ {
+ int h = Con_LineHeight(i);
+
+ // line is the last visible line?
+ *last = i;
+ if(lines_seen + h > con_backscroll && lines_seen <= con_backscroll)
+ {
+ *limitlast = lines_seen + h - con_backscroll;
+ return;
+ }
+
+ lines_seen += h;
+ }
+
+ // if we get here, no line was on screen - scroll so that one line is
+ // visible then.
+ con_backscroll = lines_seen - 1;
+ *limitlast = 1;
+}
+
+/*
+================
+Con_DrawConsole
+
+Draws the console with the solid background
+The typing input line at the bottom should only be drawn if typing is allowed
+================
+*/
+void Con_DrawConsole (int lines)
+{
+ float alpha, alpha0;
+ double sx, sy;
+ int mask_must = 0;
+ int mask_mustnot = (developer.integer>0) ? 0 : CON_MASK_DEVELOPER;
+ cachepic_t *conbackpic;
+ unsigned int conbackflags;
+
+ if (lines <= 0)
+ return;
+
+ if (con_mutex) Thread_LockMutex(con_mutex);
+
+ if (con_backscroll < 0)
+ con_backscroll = 0;
+
+ con_vislines = lines;
+
+ r_draw2d_force = true;
+
+// draw the background
+ alpha0 = cls.signon == SIGNONS ? scr_conalpha.value : 1.0f; // always full alpha when not in game
+ if((alpha = alpha0 * scr_conalphafactor.value) > 0)
+ {
+ sx = scr_conscroll_x.value;
+ sy = scr_conscroll_y.value;
+ conbackflags = CACHEPICFLAG_FAILONMISSING; // So console is readable when game content is missing
+ if (sx != 0 || sy != 0)
+ conbackflags &= CACHEPICFLAG_NOCLAMP;
+ conbackpic = scr_conbrightness.value >= 0.01f ? Draw_CachePic_Flags("gfx/conback", conbackflags) : NULL;
+ sx *= host.realtime; sy *= host.realtime;
+ sx -= floor(sx); sy -= floor(sy);
+ if (Draw_IsPicLoaded(conbackpic))
+ DrawQ_SuperPic(0, lines - vid_conheight.integer, conbackpic, vid_conwidth.integer, vid_conheight.integer,
+ 0 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 1 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 0 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 1 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 0);
+ else
+ DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0.0f, 0.0f, 0.0f, alpha, 0);
+ }
+ if((alpha = alpha0 * scr_conalpha2factor.value) > 0)
+ {
+ sx = scr_conscroll2_x.value;
+ sy = scr_conscroll2_y.value;
+ conbackpic = Draw_CachePic_Flags("gfx/conback2", (sx != 0 || sy != 0) ? CACHEPICFLAG_NOCLAMP : 0);
+ sx *= host.realtime; sy *= host.realtime;
+ sx -= floor(sx); sy -= floor(sy);
+ if(Draw_IsPicLoaded(conbackpic))
+ DrawQ_SuperPic(0, lines - vid_conheight.integer, conbackpic, vid_conwidth.integer, vid_conheight.integer,
+ 0 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 1 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 0 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 1 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 0);
+ }
+ if((alpha = alpha0 * scr_conalpha3factor.value) > 0)
+ {
+ sx = scr_conscroll3_x.value;
+ sy = scr_conscroll3_y.value;
+ conbackpic = Draw_CachePic_Flags("gfx/conback3", (sx != 0 || sy != 0) ? CACHEPICFLAG_NOCLAMP : 0);
+ sx *= host.realtime; sy *= host.realtime;
+ sx -= floor(sx); sy -= floor(sy);
+ if(Draw_IsPicLoaded(conbackpic))
+ DrawQ_SuperPic(0, lines - vid_conheight.integer, conbackpic, vid_conwidth.integer, vid_conheight.integer,
+ 0 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 1 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 0 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 1 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
+ 0);
+ }
+ DrawQ_String(vid_conwidth.integer - DrawQ_TextWidth(engineversion, 0, con_textsize.value, con_textsize.value, false, FONT_CONSOLE), lines - con_textsize.value, engineversion, 0, con_textsize.value, con_textsize.value, 1, 0, 0, 1, 0, NULL, true, FONT_CONSOLE);
+
+// draw the text
+#if 0
+ {
+ int i;
+ int count = CON_LINES_COUNT;
+ float ymax = con_vislines - 2 * con_textsize.value;
+ float y = ymax + con_textsize.value * con_backscroll;
+ for (i = 0;i < count && y >= 0;i++)
+ y -= Con_DrawConsoleLine(mask_must, mask_mustnot, y - con_textsize.value, CON_LINES_COUNT - 1 - i, 0, ymax) * con_textsize.value;
+ // fix any excessive scrollback for the next frame
+ if (i >= count && y >= 0)
+ {
+ con_backscroll -= (int)(y / con_textsize.value);
+ if (con_backscroll < 0)
+ con_backscroll = 0;
+ }
+ }
+#else
+ if(CON_LINES_COUNT > 0)
+ {
+ int i, last, limitlast;
+ float y;
+ float ymax = con_vislines - 2 * con_textsize.value;
+ Con_LastVisibleLine(mask_must, mask_mustnot, &last, &limitlast);
+ //Con_LastVisibleLine(mask_must, mask_mustnot, &last, &limitlast);
+ y = ymax - con_textsize.value;
+
+ if(limitlast)
+ y += (CON_LINES(last).height - limitlast) * con_textsize.value;
+ i = last;
+
+ for(;;)
+ {
+ y -= Con_DrawConsoleLine(mask_must, mask_mustnot, y, i, 0, ymax) * con_textsize.value;
+ if(i == 0)
+ break; // top of console buffer
+ if(y < 0)
+ break; // top of console window
+ limitlast = 0;
+ --i;
+ }
+ }
+#endif
+
+// draw the input prompt, user text, and cursor if desired
+ Con_DrawInput(true, 0, con_vislines - con_textsize.value * 2, con_textsize.value);
+
+ r_draw2d_force = false;
+ if (con_mutex) Thread_UnlockMutex(con_mutex);
+}
+
+/*
+GetMapList
+
+Made by [515]
+Prints not only map filename, but also
+its format (q1/q2/q3/hl) and even its message
+*/
+//[515]: here is an ugly hack.. two gotos... oh my... *but it works*
+//LadyHavoc: rewrote bsp type detection, rewrote message extraction to do proper worldspawn parsing
+//LadyHavoc: added .ent file loading, and redesigned error handling to still try the .ent file even if the map format is not recognized, this also eliminated one goto
+//LadyHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
+qbool GetMapList (const char *s, char *completedname, int completednamebufferlength)
+{
+ fssearch_t *t;
+ char message[1024];
+ int i, k, max, p, o, min;
+ unsigned char *len;
+ qfile_t *f;
+ unsigned char buf[1024];
+
+ dpsnprintf(message, sizeof(message), "maps/%s*.bsp", s);
+ t = FS_Search(message, 1, true, NULL);
+ if(!t)
+ return false;
+ if (t->numfilenames > 1)
+ Con_Printf("^1 %i maps found :\n", t->numfilenames);
+ len = (unsigned char *)Z_Malloc(t->numfilenames);
+ min = 666;
+ for(max=i=0;i<t->numfilenames;i++)
+ {
+ k = (int)strlen(t->filenames[i]);
+ k -= 9;
+ if(max < k)
+ max = k;
+ else
+ if(min > k)
+ min = k;
+ len[i] = k;
+ }
+ o = (int)strlen(s);
+ for(i=0;i<t->numfilenames;i++)
+ {
+ int lumpofs = 0, lumplen = 0;
+ char *entities = NULL;
+ const char *data = NULL;
+ char keyname[64];
+ char entfilename[MAX_QPATH];
+ char desc[64];
+ desc[0] = 0;
+ strlcpy(message, "^1ERROR: open failed^7", sizeof(message));
+ p = 0;
+ f = FS_OpenVirtualFile(t->filenames[i], true);
+ if(f)
+ {
+ strlcpy(message, "^1ERROR: not a known map format^7", sizeof(message));
+ memset(buf, 0, 1024);
+ FS_Read(f, buf, 1024);
+ if (!memcmp(buf, "IBSP", 4))
+ {
+ p = LittleLong(((int *)buf)[1]);
+ if (p == Q3BSPVERSION)
+ {
+ q3dheader_t *header = (q3dheader_t *)buf;
+ lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
+ lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
+ dpsnprintf(desc, sizeof(desc), "Q3BSP%i", p);
+ }
+ else if (p == Q2BSPVERSION)
+ {
+ q2dheader_t *header = (q2dheader_t *)buf;
+ lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
+ lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
+ dpsnprintf(desc, sizeof(desc), "Q2BSP%i", p);
+ }
+ else
+ dpsnprintf(desc, sizeof(desc), "IBSP%i", p);
+ }
+ else if (BuffLittleLong(buf) == BSPVERSION)
+ {
+ lumpofs = BuffLittleLong(buf + 4 + 8 * LUMP_ENTITIES);
+ lumplen = BuffLittleLong(buf + 4 + 8 * LUMP_ENTITIES + 4);
+ dpsnprintf(desc, sizeof(desc), "BSP29");
+ }
+ else if (BuffLittleLong(buf) == 30)
+ {
+ lumpofs = BuffLittleLong(buf + 4 + 8 * LUMP_ENTITIES);
+ lumplen = BuffLittleLong(buf + 4 + 8 * LUMP_ENTITIES + 4);
+ dpsnprintf(desc, sizeof(desc), "BSPHL");
+ }
+ else if (!memcmp(buf, "BSP2", 4))
+ {
+ lumpofs = BuffLittleLong(buf + 4 + 8 * LUMP_ENTITIES);
+ lumplen = BuffLittleLong(buf + 4 + 8 * LUMP_ENTITIES + 4);
+ dpsnprintf(desc, sizeof(desc), "BSP2");
+ }
+ else if (!memcmp(buf, "2PSB", 4))
+ {
+ lumpofs = BuffLittleLong(buf + 4 + 8 * LUMP_ENTITIES);
+ lumplen = BuffLittleLong(buf + 4 + 8 * LUMP_ENTITIES + 4);
+ dpsnprintf(desc, sizeof(desc), "BSP2RMQe");
+ }
+ else if(!memcmp(buf, "VBSP", 4))
+ {
+ hl2dheader_t *header = (hl2dheader_t *)buf;
+ lumpofs = LittleLong(header->lumps[HL2LUMP_ENTITIES].fileofs);
+ lumplen = LittleLong(header->lumps[HL2LUMP_ENTITIES].filelen);
+ dpsnprintf(desc, sizeof(desc), "VBSP%i", LittleLong(((int *)buf)[1]));
+ }
+ else
+ dpsnprintf(desc, sizeof(desc), "unknown%i", BuffLittleLong(buf));
+ strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
+ memcpy(entfilename + strlen(entfilename) - 4, ".ent", 5);
+ entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
+ if (!entities && lumplen >= 10)
+ {
+ FS_Seek(f, lumpofs, SEEK_SET);
+ entities = (char *)Z_Malloc(lumplen + 1);
+ FS_Read(f, entities, lumplen);
+ }
+ if (entities)
+ {
+ // if there are entities to parse, a missing message key just
+ // means there is no title, so clear the message string now
+ message[0] = 0;
+ data = entities;
+ for (;;)
+ {
+ int l;
+ if (!COM_ParseToken_Simple(&data, false, false, true))
+ break;
+ if (com_token[0] == '{')
+ continue;
+ if (com_token[0] == '}')
+ break;
+ // skip leading whitespace
+ for (k = 0;com_token[k] && ISWHITESPACE(com_token[k]);k++);
+ for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && !ISWHITESPACE(com_token[k+l]);l++)
+ keyname[l] = com_token[k+l];
+ keyname[l] = 0;
+ if (!COM_ParseToken_Simple(&data, false, false, true))
+ break;
+ if (developer_extra.integer)
+ Con_DPrintf("key: %s %s\n", keyname, com_token);
+ if (!strcmp(keyname, "message"))
+ {
+ // get the message contents
+ strlcpy(message, com_token, sizeof(message));
+ break;
+ }
+ }
+ }
+ }
+ if (entities)
+ Z_Free(entities);
+ if(f)
+ FS_Close(f);
+ *(t->filenames[i]+len[i]+5) = 0;
+ Con_Printf("%16s (%-8s) %s\n", t->filenames[i]+5, desc, message);
+ }
+ Con_Print("\n");
+ for(p=o;p<min;p++)
+ {
+ k = *(t->filenames[0]+5+p);
+ if(k == 0)
+ goto endcomplete;
+ for(i=1;i<t->numfilenames;i++)
+ if(*(t->filenames[i]+5+p) != k)
+ goto endcomplete;
+ }
+endcomplete:
+ if(p > o && completedname && completednamebufferlength > 0)
+ {
+ memset(completedname, 0, completednamebufferlength);
+ memcpy(completedname, (t->filenames[0]+5), min(p, completednamebufferlength - 1));
+ }
+ Z_Free(len);
+ FS_FreeSearch(t);
+ return p > o;
+}
+
+/*
+ Con_DisplayList
+
+ New function for tab-completion system
+ Added by EvilTypeGuy
+ MEGA Thanks to Taniwha
+
+*/
+void Con_DisplayList(const char **list)
+{
+ int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
+ const char **walk = list;