]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
9520cb3f039ecb4c6fd12f09865eb5fb3d4c5869
[xonotic/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27
28 #include "quakedef.h"
29
30 cvar_t registered = {0, "registered","0"};
31 cvar_t cmdline = {0, "cmdline","0"};
32
33 extern qboolean fs_modified;   // set true if using non-id files
34
35 char com_token[1024];
36 int com_argc;
37 const char **com_argv;
38
39 // LordHavoc: made commandline 1024 characters instead of 256
40 #define CMDLINE_LENGTH  1024
41 char com_cmdline[CMDLINE_LENGTH];
42
43 int gamemode;
44 char *gamename;
45 char *gamedirname;
46 char com_modname[MAX_OSPATH];
47
48
49 /*
50 ============================================================================
51
52                                         BYTE ORDER FUNCTIONS
53
54 ============================================================================
55 */
56
57 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
58 short   (*BigShort) (short l);
59 short   (*LittleShort) (short l);
60 int     (*BigLong) (int l);
61 int     (*LittleLong) (int l);
62 float   (*BigFloat) (float l);
63 float   (*LittleFloat) (float l);
64 #endif
65
66 short   ShortSwap (short l)
67 {
68         qbyte    b1,b2;
69
70         b1 = l&255;
71         b2 = (l>>8)&255;
72
73         return (b1<<8) + b2;
74 }
75
76 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
77 short   ShortNoSwap (short l)
78 {
79         return l;
80 }
81 #endif
82
83 int    LongSwap (int l)
84 {
85         qbyte    b1,b2,b3,b4;
86
87         b1 = l&255;
88         b2 = (l>>8)&255;
89         b3 = (l>>16)&255;
90         b4 = (l>>24)&255;
91
92         return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
93 }
94
95 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
96 int     LongNoSwap (int l)
97 {
98         return l;
99 }
100 #endif
101
102 float FloatSwap (float f)
103 {
104         union
105         {
106                 float   f;
107                 qbyte    b[4];
108         } dat1, dat2;
109
110
111         dat1.f = f;
112         dat2.b[0] = dat1.b[3];
113         dat2.b[1] = dat1.b[2];
114         dat2.b[2] = dat1.b[1];
115         dat2.b[3] = dat1.b[0];
116         return dat2.f;
117 }
118
119 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
120 float FloatNoSwap (float f)
121 {
122         return f;
123 }
124 #endif
125
126
127 // Extract integers from buffers
128
129 unsigned int BuffBigLong (const qbyte *buffer)
130 {
131         return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
132 }
133
134 unsigned short BuffBigShort (const qbyte *buffer)
135 {
136         return (buffer[0] << 8) | buffer[1];
137 }
138
139 unsigned int BuffLittleLong (const qbyte *buffer)
140 {
141         return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
142 }
143
144 unsigned short BuffLittleShort (const qbyte *buffer)
145 {
146         return (buffer[1] << 8) | buffer[0];
147 }
148
149
150 /*
151 ==============================================================================
152
153                         MESSAGE IO FUNCTIONS
154
155 Handles byte ordering and avoids alignment errors
156 ==============================================================================
157 */
158
159 //
160 // writing functions
161 //
162
163 void MSG_WriteChar (sizebuf_t *sb, int c)
164 {
165         qbyte    *buf;
166
167         buf = SZ_GetSpace (sb, 1);
168         buf[0] = c;
169 }
170
171 void MSG_WriteByte (sizebuf_t *sb, int c)
172 {
173         qbyte    *buf;
174
175         buf = SZ_GetSpace (sb, 1);
176         buf[0] = c;
177 }
178
179 void MSG_WriteShort (sizebuf_t *sb, int c)
180 {
181         qbyte    *buf;
182
183         buf = SZ_GetSpace (sb, 2);
184         buf[0] = c&0xff;
185         buf[1] = c>>8;
186 }
187
188 void MSG_WriteLong (sizebuf_t *sb, int c)
189 {
190         qbyte    *buf;
191
192         buf = SZ_GetSpace (sb, 4);
193         buf[0] = c&0xff;
194         buf[1] = (c>>8)&0xff;
195         buf[2] = (c>>16)&0xff;
196         buf[3] = c>>24;
197 }
198
199 void MSG_WriteFloat (sizebuf_t *sb, float f)
200 {
201         union
202         {
203                 float   f;
204                 int     l;
205         } dat;
206
207
208         dat.f = f;
209         dat.l = LittleLong (dat.l);
210
211         SZ_Write (sb, &dat.l, 4);
212 }
213
214 void MSG_WriteString (sizebuf_t *sb, const char *s)
215 {
216         if (!s)
217                 SZ_Write (sb, "", 1);
218         else
219                 SZ_Write (sb, s, strlen(s)+1);
220 }
221
222 // used by server (always latest dpprotocol)
223 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
224 {
225         if (f >= 0)
226                 MSG_WriteShort (sb, (int)(f + 0.5f));
227         else
228                 MSG_WriteShort (sb, (int)(f - 0.5f));
229 }
230
231 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
232 {
233         if (f >= 0)
234                 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
235         else
236                 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
237 }
238
239 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
240 void MSG_WriteAngle (sizebuf_t *sb, float f)
241 {
242         if (f >= 0)
243                 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
244         else
245                 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
246 }
247
248 //
249 // reading functions
250 //
251 int msg_readcount;
252 qboolean msg_badread;
253
254 void MSG_BeginReading (void)
255 {
256         msg_readcount = 0;
257         msg_badread = false;
258 }
259
260 int MSG_ReadLittleShort (void)
261 {
262         if (msg_readcount+2 > net_message.cursize)
263         {
264                 msg_badread = true;
265                 return -1;
266         }
267         msg_readcount += 2;
268         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
269 }
270
271 int MSG_ReadBigShort (void)
272 {
273         if (msg_readcount+2 > net_message.cursize)
274         {
275                 msg_badread = true;
276                 return -1;
277         }
278         msg_readcount += 2;
279         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
280 }
281
282 int MSG_ReadLittleLong (void)
283 {
284         if (msg_readcount+4 > net_message.cursize)
285         {
286                 msg_badread = true;
287                 return -1;
288         }
289         msg_readcount += 4;
290         return net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
291 }
292
293 int MSG_ReadBigLong (void)
294 {
295         if (msg_readcount+4 > net_message.cursize)
296         {
297                 msg_badread = true;
298                 return -1;
299         }
300         msg_readcount += 4;
301         return (net_message.data[msg_readcount-4]<<24) + (net_message.data[msg_readcount-3]<<16) + (net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1];
302 }
303
304 float MSG_ReadLittleFloat (void)
305 {
306         union
307         {
308                 float f;
309                 int l;
310         } dat;
311         if (msg_readcount+4 > net_message.cursize)
312         {
313                 msg_badread = true;
314                 return -1;
315         }
316         msg_readcount += 4;
317         dat.l = net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
318         return dat.f;
319 }
320
321 float MSG_ReadBigFloat (void)
322 {
323         union
324         {
325                 float f;
326                 int l;
327         } dat;
328         if (msg_readcount+4 > net_message.cursize)
329         {
330                 msg_badread = true;
331                 return -1;
332         }
333         msg_readcount += 4;
334         dat.l = (net_message.data[msg_readcount-4]<<24) | (net_message.data[msg_readcount-3]<<16) | (net_message.data[msg_readcount-2]<<8) | net_message.data[msg_readcount-1];
335         return dat.f;
336 }
337
338 char *MSG_ReadString (void)
339 {
340         static char string[2048];
341         int l,c;
342         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++)
343                 string[l] = c;
344         string[l] = 0;
345         return string;
346 }
347
348 int MSG_ReadBytes (int numbytes, unsigned char *out)
349 {
350         int l, c;
351         for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++)
352                 out[l] = c;
353         return l;
354 }
355
356 // used by server (always latest dpprotocol)
357 float MSG_ReadDPCoord (void)
358 {
359         return (signed short) MSG_ReadLittleShort();
360 }
361
362 // used by client
363 float MSG_ReadCoord (void)
364 {
365         if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
366                 return (signed short) MSG_ReadLittleShort();
367         else if (dpprotocol == DPPROTOCOL_VERSION1)
368                 return MSG_ReadLittleFloat();
369         else
370                 return MSG_ReadLittleShort() * (1.0f/8.0f);
371 }
372
373
374 //===========================================================================
375
376 void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name)
377 {
378         if (startsize < 256)
379                 startsize = 256;
380         buf->mempool = Mem_AllocPool(name);
381         buf->data = Mem_Alloc(buf->mempool, startsize);
382         buf->maxsize = startsize;
383         buf->cursize = 0;
384 }
385
386
387 void SZ_Free (sizebuf_t *buf)
388 {
389         Mem_FreePool(&buf->mempool);
390         buf->data = NULL;
391         buf->maxsize = 0;
392         buf->cursize = 0;
393 }
394
395 void SZ_Clear (sizebuf_t *buf)
396 {
397         buf->cursize = 0;
398 }
399
400 void *SZ_GetSpace (sizebuf_t *buf, int length)
401 {
402         void *data;
403
404         if (buf->cursize + length > buf->maxsize)
405         {
406                 if (!buf->allowoverflow)
407                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
408
409                 if (length > buf->maxsize)
410                         Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
411
412                 buf->overflowed = true;
413                 Con_Printf ("SZ_GetSpace: overflow\n");
414                 SZ_Clear (buf);
415         }
416
417         data = buf->data + buf->cursize;
418         buf->cursize += length;
419
420         return data;
421 }
422
423 void SZ_Write (sizebuf_t *buf, const void *data, int length)
424 {
425         memcpy (SZ_GetSpace(buf,length),data,length);
426 }
427
428 void SZ_Print (sizebuf_t *buf, const char *data)
429 {
430         int len;
431         len = strlen(data)+1;
432
433 // byte * cast to keep VC++ happy
434         if (buf->data[buf->cursize-1])
435                 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
436         else
437                 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
438 }
439
440 static char *hexchar = "0123456789ABCDEF";
441 void Com_HexDumpToConsole(const qbyte *data, int size)
442 {
443         int i, j, n;
444         char text[1024];
445         char *cur, *flushpointer;
446         const qbyte *d;
447         cur = text;
448         flushpointer = text + 512;
449         for (i = 0;i < size;)
450         {
451                 n = 16;
452                 if (n > size - i)
453                         n = size - i;
454                 d = data + i;
455                 *cur++ = hexchar[(i >> 12) & 15];
456                 *cur++ = hexchar[(i >>  8) & 15];
457                 *cur++ = hexchar[(i >>  4) & 15];
458                 *cur++ = hexchar[(i >>  0) & 15];
459                 *cur++ = ':';
460                 for (j = 0;j < n;j++)
461                 {
462                         if (j & 1)
463                         {
464                                 *cur++ = hexchar[(d[j] >> 4) & 15] | 0x80;
465                                 *cur++ = hexchar[(d[j] >> 0) & 15] | 0x80;
466                         }
467                         else
468                         {
469                                 *cur++ = hexchar[(d[j] >> 4) & 15];
470                                 *cur++ = hexchar[(d[j] >> 0) & 15];
471                         }
472                 }
473                 for (;j < 16;j++)
474                 {
475                         *cur++ = ' ';
476                         *cur++ = ' ';
477                 }
478                 for (j = 0;j < n;j++)
479                         *cur++ = (d[j] >= ' ' && d[j] <= 0x7E) ? d[j] : '.';
480                 for (;j < 16;j++)
481                         *cur++ = ' ';
482                 *cur++ = '\n';
483                 i += n;
484                 if (cur >= flushpointer || i >= size)
485                 {
486                         *cur++ = 0;
487                         Con_Printf("%s", text);
488                         cur = text;
489                 }
490         }
491 }
492
493 void SZ_HexDumpToConsole(const sizebuf_t *buf)
494 {
495         Com_HexDumpToConsole(buf->data, buf->cursize);
496 }
497
498
499 //============================================================================
500
501
502 /*
503 ==============
504 COM_ParseToken
505
506 Parse a token out of a string
507 ==============
508 */
509 int COM_ParseToken (const char **datapointer)
510 {
511         int c;
512         int len;
513         const char *data = *datapointer;
514
515         len = 0;
516         com_token[0] = 0;
517
518         if (!data)
519         {
520                 *datapointer = NULL;
521                 return false;
522         }
523
524 // skip whitespace
525 skipwhite:
526         while ((c = *data) <= ' ')
527         {
528                 if (c == 0)
529                 {
530                         // end of file
531                         *datapointer = NULL;
532                         return false;
533                 }
534                 data++;
535         }
536
537 // skip // comments
538         if (c=='/' && data[1] == '/')
539         {
540                 while (*data && *data != '\n')
541                         data++;
542                 goto skipwhite;
543         }
544
545
546 // handle quoted strings specially
547         if (c == '\"')
548         {
549                 data++;
550                 while (1)
551                 {
552                         c = *data++;
553                         if (c=='\"' || !c)
554                         {
555                                 com_token[len] = 0;
556                                 *datapointer = data;
557                                 return true;
558                         }
559                         com_token[len] = c;
560                         len++;
561                 }
562         }
563
564 // parse single characters
565         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
566         {
567                 com_token[len] = c;
568                 len++;
569                 com_token[len] = 0;
570                 *datapointer = data+1;
571                 return true;
572         }
573
574 // parse a regular word
575         do
576         {
577                 com_token[len] = c;
578                 data++;
579                 len++;
580                 c = *data;
581                 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
582                         break;
583         } while (c>32);
584
585         com_token[len] = 0;
586         *datapointer = data;
587         return true;
588 }
589
590
591 /*
592 ================
593 COM_CheckParm
594
595 Returns the position (1 to argc-1) in the program's argument list
596 where the given parameter apears, or 0 if not present
597 ================
598 */
599 int COM_CheckParm (const char *parm)
600 {
601         int i;
602
603         for (i=1 ; i<com_argc ; i++)
604         {
605                 if (!com_argv[i])
606                         continue;               // NEXTSTEP sometimes clears appkit vars.
607                 if (!strcmp (parm,com_argv[i]))
608                         return i;
609         }
610
611         return 0;
612 }
613
614 /*
615 ================
616 COM_CheckRegistered
617
618 Looks for the pop.txt file and verifies it.
619 Sets the "registered" cvar.
620 Immediately exits out if an alternate game was attempted to be started without
621 being registered.
622 ================
623 */
624 void COM_CheckRegistered (void)
625 {
626         Cvar_Set ("cmdline", com_cmdline);
627
628         if (!FS_FileExists("gfx/pop.lmp"))
629         {
630                 if (fs_modified)
631                         Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
632                 else
633                         Con_Printf ("Playing shareware version.\n");
634                 return;
635         }
636
637         Cvar_Set ("registered", "1");
638         Con_Printf ("Playing registered version.\n");
639 }
640
641
642 /*
643 ================
644 COM_InitArgv
645 ================
646 */
647 void COM_InitArgv (void)
648 {
649         int i, j, n;
650         // reconstitute the command line for the cmdline externally visible cvar
651         n = 0;
652         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
653         {
654                 i = 0;
655                 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
656                         com_cmdline[n++] = com_argv[j][i++];
657                 if (n < (CMDLINE_LENGTH - 1))
658                         com_cmdline[n++] = ' ';
659                 else
660                         break;
661         }
662         com_cmdline[n] = 0;
663 }
664
665 void COM_InitGameType (void)
666 {
667         char name[MAX_OSPATH];
668         FS_StripExtension(com_argv[0], name);
669         COM_ToLowerString(name, name);
670
671         if (strstr(name, "transfusion"))
672                 gamemode = GAME_TRANSFUSION;
673         else if (strstr(name, "nexuiz"))
674                 gamemode = GAME_NEXUIZ;
675         else if (strstr(name, "nehahra"))
676                 gamemode = GAME_NEHAHRA;
677         else if (strstr(name, "hipnotic"))
678                 gamemode = GAME_HIPNOTIC;
679         else if (strstr(name, "rogue"))
680                 gamemode = GAME_ROGUE;
681         else if (strstr(name, "gvb2"))
682                 gamemode = GAME_GOODVSBAD2;
683         else if (strstr(name, "teu"))
684                 gamemode = GAME_TEU;
685         else if (strstr(name, "battlemech"))
686                 gamemode = GAME_BATTLEMECH;
687         else
688                 gamemode = GAME_NORMAL;
689
690         if (COM_CheckParm ("-transfusion"))
691                 gamemode = GAME_TRANSFUSION;
692         else if (COM_CheckParm ("-nexuiz"))
693                 gamemode = GAME_NEXUIZ;
694         else if (COM_CheckParm ("-nehahra"))
695                 gamemode = GAME_NEHAHRA;
696         else if (COM_CheckParm ("-hipnotic"))
697                 gamemode = GAME_HIPNOTIC;
698         else if (COM_CheckParm ("-rogue"))
699                 gamemode = GAME_ROGUE;
700         else if (COM_CheckParm ("-quake"))
701                 gamemode = GAME_NORMAL;
702         else if (COM_CheckParm ("-goodvsbad2"))
703                 gamemode = GAME_GOODVSBAD2;
704         else if (COM_CheckParm ("-teu"))
705                 gamemode = GAME_TEU;
706         else if (COM_CheckParm ("-battlemech"))
707                 gamemode = GAME_BATTLEMECH;
708
709         switch(gamemode)
710         {
711         case GAME_NORMAL:
712                 gamename = "DarkPlaces-Quake";
713                 gamedirname = "";
714                 break;
715         case GAME_HIPNOTIC:
716                 gamename = "Darkplaces-Hipnotic";
717                 gamedirname = "hipnotic";
718                 break;
719         case GAME_ROGUE:
720                 gamename = "Darkplaces-Rogue";
721                 gamedirname = "rogue";
722                 break;
723         case GAME_NEHAHRA:
724                 gamename = "DarkPlaces-Nehahra";
725                 gamedirname = "nehahra";
726                 break;
727         case GAME_NEXUIZ:
728                 gamename = "Nexuiz";
729                 gamedirname = "data";
730                 break;
731         case GAME_TRANSFUSION:
732                 gamename = "Transfusion";
733                 gamedirname = "transfusion";
734                 break;
735         case GAME_GOODVSBAD2:
736                 gamename = "GoodVs.Bad2";
737                 gamedirname = "rts";
738                 break;
739         case GAME_TEU:
740                 gamename = "TheEvilUnleashed";
741                 gamedirname = "teu";
742                 break;
743         case GAME_BATTLEMECH:
744                 gamename = "Battlemech";
745                 gamedirname = "battlemech";
746                 break;
747         default:
748                 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
749                 break;
750         }
751 }
752
753
754 extern void Mathlib_Init(void);
755 extern void FS_Init (void);
756
757 /*
758 ================
759 COM_Init
760 ================
761 */
762 void COM_Init (void)
763 {
764 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
765         qbyte swaptest[2] = {1,0};
766
767 // set the byte swapping variables in a portable manner
768         if ( *(short *)swaptest == 1)
769         {
770                 BigShort = ShortSwap;
771                 LittleShort = ShortNoSwap;
772                 BigLong = LongSwap;
773                 LittleLong = LongNoSwap;
774                 BigFloat = FloatSwap;
775                 LittleFloat = FloatNoSwap;
776         }
777         else
778         {
779                 BigShort = ShortNoSwap;
780                 LittleShort = ShortSwap;
781                 BigLong = LongNoSwap;
782                 LittleLong = LongSwap;
783                 BigFloat = FloatNoSwap;
784                 LittleFloat = FloatSwap;
785         }
786 #endif
787
788         Cvar_RegisterVariable (&registered);
789         Cvar_RegisterVariable (&cmdline);
790
791         Mathlib_Init();
792
793         FS_Init ();
794         Con_InitLogging();
795         COM_CheckRegistered ();
796
797         COM_InitGameType();
798 }
799
800
801 /*
802 ============
803 va
804
805 does a varargs printf into a temp buffer, so I don't need to have
806 varargs versions of all text functions.
807 FIXME: make this buffer size safe someday
808 ============
809 */
810 char *va(const char *format, ...)
811 {
812         va_list argptr;
813         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
814         static char string[8][1024], *s;
815         static int stringindex = 0;
816
817         s = string[stringindex];
818         stringindex = (stringindex + 1) & 7;
819         va_start (argptr, format);
820         vsprintf (s, format,argptr);
821         va_end (argptr);
822
823         return s;
824 }
825
826
827 //======================================
828 // LordHavoc: added these because they are useful
829
830 void COM_ToLowerString(const char *in, char *out)
831 {
832         while (*in)
833         {
834                 if (*in >= 'A' && *in <= 'Z')
835                         *out++ = *in++ + 'a' - 'A';
836                 else
837                         *out++ = *in++;
838         }
839 }
840
841 void COM_ToUpperString(const char *in, char *out)
842 {
843         while (*in)
844         {
845                 if (*in >= 'a' && *in <= 'z')
846                         *out++ = *in++ + 'A' - 'a';
847                 else
848                         *out++ = *in++;
849         }
850 }
851
852 int COM_StringBeginsWith(const char *s, const char *match)
853 {
854         for (;*s && *match;s++, match++)
855                 if (*s != *match)
856                         return false;
857         return true;
858 }
859
860 // written by Elric, thanks Elric!
861 char *SearchInfostring(const char *infostring, const char *key)
862 {
863         static char value [256];
864         char crt_key [256];
865         size_t value_ind, key_ind;
866         char c;
867
868         if (*infostring++ != '\\')
869                 return NULL;
870
871         value_ind = 0;
872         for (;;)
873         {
874                 key_ind = 0;
875
876                 // Get the key name
877                 for (;;)
878                 {
879                         c = *infostring++;
880
881                         if (c == '\0')
882                                 return NULL;
883                         if (c == '\\')
884                         {
885                                 crt_key[key_ind] = '\0';
886                                 break;
887                         }
888
889                         crt_key[key_ind++] = c;
890                 }
891
892                 // If it's the key we are looking for, save it in "value"
893                 if (!strcmp(crt_key, key))
894                 {
895                         for (;;)
896                         {
897                                 c = *infostring++;
898
899                                 if (c == '\0' || c == '\\')
900                                 {
901                                         value[value_ind] = '\0';
902                                         return value;
903                                 }
904
905                                 value[value_ind++] = c;
906                         }
907                 }
908
909                 // Else, skip the value
910                 for (;;)
911                 {
912                         c = *infostring++;
913
914                         if (c == '\0')
915                                 return NULL;
916                         if (c == '\\')
917                                 break;
918                 }
919         }
920 }
921