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 "quakedef.h" 23 24 #define NUM_SAFE_ARGVS 7 25 26 static const char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1]; 27 static const char *argvdummy = " "; 28 29 static const char *safeargvs[NUM_SAFE_ARGVS] = 30 {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"}; 31 32 cvar_t registered = CVAR2("registered","0"); 33 cvar_t cmdline = CVAR4("cmdline","0", false, true); 34 35 qboolean com_modified; // set true if using non-id files 36 37 qboolean proghack; 38 39 int static_registered = 1; // only for startup check, then set 40 41 qboolean msg_suppress_1 = 0; 42 43 void COM_InitFilesystem (void); 44 45 // if a packfile directory differs from this, it is assumed to be hacked 46 #define PAK0_COUNT 339 47 #define PAK0_CRC 32981 48 49 char com_token[1024]; 50 int com_argc; 51 const char **com_argv; 52 53 #define CMDLINE_LENGTH 256 54 char com_cmdline[CMDLINE_LENGTH]; 55 56 qboolean standard_quake = true, rogue, hipnotic; 57 58 // this graphic needs to be in the pak file to use registered features 59 unsigned short pop[] = 60 { 61 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 62 ,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000 63 ,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000 64 ,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600 65 ,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563 66 ,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564 67 ,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564 68 ,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563 69 ,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500 70 ,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200 71 ,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000 72 ,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000 73 ,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000 74 ,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000 75 ,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000 76 ,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000 77 }; 78 79 /* 80 81 82 All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. 83 84 The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is 85 only used during filesystem initialization. 86 87 The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't. 88 89 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory 90 specified, when a file is found by the normal search path, it will be mirrored 91 into the cache directory, then opened there. 92 93 94 95 FIXME: 96 The file "parms.txt" will be read out of the game directory and appended to the current command line arguments to allow different games to initialize startup parms differently. This could be used to add a "-sspeed 22050" for the high quality sound edition. Because they are added at the end, they will not override an explicit setting on the original command line. 97 98 */ 99 100 //============================================================================ 101 102 103 // ClearLink is used for new headnodes 104 void ClearLink (link_t *l) 105 { 106 l->prev = l->next = l; 107 } 108 109 void RemoveLink (link_t *l) 110 { 111 l->next->prev = l->prev; 112 l->prev->next = l->next; 113 } 114 115 void InsertLinkBefore (link_t *l, link_t *before) 116 { 117 l->next = before; 118 l->prev = before->prev; 119 l->prev->next = l; 120 l->next->prev = l; 121 } 122 void InsertLinkAfter (link_t *l, link_t *after) 123 { 124 l->next = after->next; 125 l->prev = after; 126 l->prev->next = l; 127 l->next->prev = l; 128 } 129 130 /* 131 ============================================================================ 132 133 LIBRARY REPLACEMENT FUNCTIONS 134 135 ============================================================================ 136 */ 137 138 void Q_memset (void *dest, int fill, int count) 139 { 140 int i; 141 142 if ( (((long)dest | count) & 3) == 0) 143 { 144 count >>= 2; 145 fill = fill | (fill<<8) | (fill<<16) | (fill<<24); 146 for (i=0 ; i<count ; i++) 147 ((int *)dest)[i] = fill; 148 } 149 else 150 for (i=0 ; i<count ; i++) 151 ((byte *)dest)[i] = fill; 152 } 153 154 void Q_memcpy (void *dest, const void *src, int count) 155 { 156 int i; 157 158 if (( ( (long)dest | (long)src | count) & 3) == 0 ) 159 { 160 count>>=2; 161 for (i=0 ; i<count ; i++) 162 ((int *)dest)[i] = ((int *)src)[i]; 163 } 164 else 165 for (i=0 ; i<count ; i++) 166 ((byte *)dest)[i] = ((byte *)src)[i]; 167 } 168 169 int Q_memcmp (const void *m1, const void *m2, int count) 170 { 171 while(count) 172 { 173 count--; 174 if (((byte *)m1)[count] != ((byte *)m2)[count]) 175 return -1; 176 } 177 return 0; 178 } 179 180 void Q_strcpy (char *dest, const char *src) 181 { 182 while (*src) 183 { 184 *dest++ = *src++; 185 } 186 *dest++ = 0; 187 } 188 189 void Q_strncpy (char *dest, const char *src, int count) 190 { 191 while (*src && count--) 192 { 193 *dest++ = *src++; 194 } 195 if (count) 196 *dest++ = 0; 197 } 198 199 int Q_strlen (const char *str) 200 { 201 int count; 202 203 count = 0; 204 while (str[count]) 205 count++; 206 207 return count; 208 } 209 210 char *Q_strrchr(const char *s, char c) 211 { 212 int len = Q_strlen(s); 213 s += len; 214 while (len--) 215 if (*--s == c) return (char*) s; 216 return 0; 217 } 218 219 void Q_strcat (char *dest, const char *src) 220 { 221 dest += Q_strlen(dest); 222 Q_strcpy (dest, src); 223 } 224 225 int Q_strcmp (const char *s1, const char *s2) 226 { 227 while (1) 228 { 229 if (*s1 != *s2) 230 return -1; // strings not equal 231 if (!*s1) 232 return 0; // strings are equal 233 s1++; 234 s2++; 235 } 236 237 return -1; 238 } 239 240 int Q_strncmp (const char *s1, const char *s2, int count) 241 { 242 while (1) 243 { 244 if (!count--) 245 return 0; 246 if (*s1 != *s2) 247 return -1; // strings not equal 248 if (!*s1) 249 return 0; // strings are equal 250 s1++; 251 s2++; 252 } 253 254 return -1; 255 } 256 257 int Q_strncasecmp (const char *s1, const char *s2, int n) 258 { 259 int c1, c2; 260 261 while (1) 262 { 263 c1 = *s1++; 264 c2 = *s2++; 265 266 if (!n--) 267 return 0; // strings are equal until end point 268 269 if (c1 != c2) 270 { 271 if (c1 >= 'a' && c1 <= 'z') 272 c1 -= ('a' - 'A'); 273 if (c2 >= 'a' && c2 <= 'z') 274 c2 -= ('a' - 'A'); 275 if (c1 != c2) 276 return -1; // strings not equal 277 } 278 if (!c1) 279 return 0; // strings are equal 280 // s1++; 281 // s2++; 282 } 283 284 return -1; 285 } 286 287 int Q_strcasecmp (const char *s1, const char *s2) 288 { 289 return Q_strncasecmp (s1, s2, 99999); 290 } 291 292 int Q_atoi (const char *str) 293 { 294 int val; 295 int sign; 296 int c; 297 298 if (*str == '-') 299 { 300 sign = -1; 301 str++; 302 } 303 else 304 sign = 1; 305 306 val = 0; 307 308 // 309 // check for hex 310 // 311 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) 312 { 313 str += 2; 314 while (1) 315 { 316 c = *str++; 317 if (c >= '0' && c <= '9') 318 val = (val<<4) + c - '0'; 319 else if (c >= 'a' && c <= 'f') 320 val = (val<<4) + c - 'a' + 10; 321 else if (c >= 'A' && c <= 'F') 322 val = (val<<4) + c - 'A' + 10; 323 else 324 return val*sign; 325 } 326 } 327 328 // 329 // check for character 330 // 331 if (str[0] == '\'') 332 { 333 return sign * str[1]; 334 } 335 336 // 337 // assume decimal 338 // 339 while (1) 340 { 341 c = *str++; 342 if (c <'0' || c > '9') 343 return val*sign; 344 val = val*10 + c - '0'; 345 } 346 347 return 0; 348 } 349 350 351 float Q_atof (const char *str) 352 { 353 double val; 354 int sign; 355 int c; 356 int decimal, total; 357 358 if (*str == '-') 359 { 360 sign = -1; 361 str++; 362 } 363 else 364 sign = 1; 365 366 val = 0; 367 368 // 369 // check for hex 370 // 371 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) 372 { 373 str += 2; 374 while (1) 375 { 376 c = *str++; 377 if (c >= '0' && c <= '9') 378 val = (val*16) + c - '0'; 379 else if (c >= 'a' && c <= 'f') 380 val = (val*16) + c - 'a' + 10; 381 else if (c >= 'A' && c <= 'F') 382 val = (val*16) + c - 'A' + 10; 383 else 384 return val*sign; 385 } 386 } 387 388 // 389 // check for character 390 // 391 if (str[0] == '\'') 392 { 393 return sign * str[1]; 394 } 395 396 // 397 // assume decimal 398 // 399 decimal = -1; 400 total = 0; 401 while (1) 402 { 403 c = *str++; 404 if (c == '.') 405 { 406 decimal = total; 407 continue; 408 } 409 if (c <'0' || c > '9') 410 break; 411 val = val*10 + c - '0'; 412 total++; 413 } 414 415 if (decimal == -1) 416 return val*sign; 417 while (total > decimal) 418 { 419 val /= 10; 420 total--; 421 } 422 423 return val*sign; 424 } 425 426 /* 427 ============================================================================ 428 429 BYTE ORDER FUNCTIONS 430 431 ============================================================================ 432 */ 433 434 qboolean bigendien; 435 436 short (*BigShort) (short l); 437 short (*LittleShort) (short l); 438 int (*BigLong) (int l); 439 int (*LittleLong) (int l); 440 float (*BigFloat) (float l); 441 float (*LittleFloat) (float l); 442 443 short ShortSwap (short l) 444 { 445 byte b1,b2; 446 447 b1 = l&255; 448 b2 = (l>>8)&255; 449 450 return (b1<<8) + b2; 451 } 452 453 short ShortNoSwap (short l) 454 { 455 return l; 456 } 457 458 int LongSwap (int l) 459 { 460 byte b1,b2,b3,b4; 461 462 b1 = l&255; 463 b2 = (l>>8)&255; 464 b3 = (l>>16)&255; 465 b4 = (l>>24)&255; 466 467 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; 468 } 469 470 int LongNoSwap (int l) 471 { 472 return l; 473 } 474 475 float FloatSwap (float f) 476 { 477 union 478 { 479 float f; 480 byte b[4]; 481 } dat1, dat2; 482 483 484 dat1.f = f; 485 dat2.b[0] = dat1.b[3]; 486 dat2.b[1] = dat1.b[2]; 487 dat2.b[2] = dat1.b[1]; 488 dat2.b[3] = dat1.b[0]; 489 return dat2.f; 490 } 491 492 float FloatNoSwap (float f) 493 { 494 return f; 495 } 496 497 /* 498 ============================================================================== 499 500 MESSAGE IO FUNCTIONS 501 502 Handles byte ordering and avoids alignment errors 503 ============================================================================== 504 */ 505 506 // 507 // writing functions 508 // 509 510 void MSG_WriteChar (sizebuf_t *sb, int c) 511 { 512 byte *buf; 513 514 #ifdef PARANOID 515 if (c < -128 || c > 127) 516 Sys_Error ("MSG_WriteChar: range error"); 517 #endif 518 519 buf = (byte*) SZ_GetSpace (sb, 1); 520 buf[0] = c; 521 } 522 523 void MSG_WriteByte (sizebuf_t *sb, int c) 524 { 525 byte *buf; 526 527 #ifdef PARANOID 528 if (c < 0 || c > 255) 529 Sys_Error ("MSG_WriteByte: range error"); 530 #endif 531 532 buf = (byte*) SZ_GetSpace (sb, 1); 533 buf[0] = c; 534 } 535 536 void MSG_WriteShort (sizebuf_t *sb, int c) 537 { 538 byte *buf; 539 540 #ifdef PARANOID 541 if (c < ((short)0x8000) || c > (short)0x7fff) 542 Sys_Error ("MSG_WriteShort: range error"); 543 #endif 544 545 buf = (byte*) SZ_GetSpace (sb, 2); 546 buf[0] = c&0xff; 547 buf[1] = c>>8; 548 } 549 550 void MSG_WriteLong (sizebuf_t *sb, int c) 551 { 552 byte *buf; 553 554 buf = (byte*) SZ_GetSpace (sb, 4); 555 buf[0] = c&0xff; 556 buf[1] = (c>>8)&0xff; 557 buf[2] = (c>>16)&0xff; 558 buf[3] = c>>24; 559 } 560 561 void MSG_WriteFloat (sizebuf_t *sb, float f) 562 { 563 union 564 { 565 float f; 566 int l; 567 } dat; 568 569 570 dat.f = f; 571 dat.l = LittleLong (dat.l); 572 573 SZ_Write (sb, &dat.l, 4); 574 } 575 576 void MSG_WriteString (sizebuf_t *sb, const char *s) 577 { 578 if (!s) 579 SZ_Write (sb, "", 1); 580 else 581 SZ_Write (sb, s, Q_strlen(s)+1); 582 } 583 584 void MSG_WriteCoord (sizebuf_t *sb, float f) 585 { 586 MSG_WriteShort (sb, (int)(f*8)); 587 } 588 589 void MSG_WriteAngle (sizebuf_t *sb, float f) 590 { 591 MSG_WriteByte (sb, ((int)f*256/360) & 255); 592 } 593 594 // 595 // reading functions 596 // 597 int msg_readcount; 598 qboolean msg_badread; 599 600 void MSG_BeginReading (void) 601 { 602 msg_readcount = 0; 603 msg_badread = false; 604 } 605 606 // returns -1 and sets msg_badread if no more characters are available 607 int MSG_ReadChar (void) 608 { 609 int c; 610 611 if (msg_readcount+1 > net_message.cursize) 612 { 613 msg_badread = true; 614 return -1; 615 } 616 617 c = (signed char)net_message.data[msg_readcount]; 618 msg_readcount++; 619 620 return c; 621 } 622 623 int MSG_ReadByte (void) 624 { 625 int c; 626 627 if (msg_readcount+1 > net_message.cursize) 628 { 629 msg_badread = true; 630 return -1; 631 } 632 633 c = (unsigned char)net_message.data[msg_readcount]; 634 msg_readcount++; 635 636 return c; 637 } 638 639 int MSG_ReadShort (void) 640 { 641 int c; 642 643 if (msg_readcount+2 > net_message.cursize) 644 { 645 msg_badread = true; 646 return -1; 647 } 648 649 c = (short)(net_message.data[msg_readcount] 650 + (net_message.data[msg_readcount+1]<<8)); 651 652 msg_readcount += 2; 653 654 return c; 655 } 656 657 int MSG_ReadLong (void) 658 { 659 int c; 660 661 if (msg_readcount+4 > net_message.cursize) 662 { 663 msg_badread = true; 664 return -1; 665 } 666 667 c = net_message.data[msg_readcount] 668 + (net_message.data[msg_readcount+1]<<8) 669 + (net_message.data[msg_readcount+2]<<16) 670 + (net_message.data[msg_readcount+3]<<24); 671 672 msg_readcount += 4; 673 674 return c; 675 } 676 677 float MSG_ReadFloat (void) 678 { 679 union 680 { 681 byte b[4]; 682 float f; 683 int l; 684 } dat; 685 686 dat.b[0] = net_message.data[msg_readcount]; 687 dat.b[1] = net_message.data[msg_readcount+1]; 688 dat.b[2] = net_message.data[msg_readcount+2]; 689 dat.b[3] = net_message.data[msg_readcount+3]; 690 msg_readcount += 4; 691 692 dat.l = LittleLong (dat.l); 693 694 return dat.f; 695 } 696 697 char *MSG_ReadString (void) 698 { 699 static char string[2048]; 700 int l,c; 701 702 l = 0; 703 do 704 { 705 c = MSG_ReadChar (); 706 if (c == -1 || c == 0) 707 break; 708 string[l] = c; 709 l++; 710 } while (l < (int) (sizeof(string)-1)); 711 712 string[l] = 0; 713 714 return string; 715 } 716 717 float MSG_ReadCoord (void) 718 { 719 return MSG_ReadShort() * (1.0/8); 720 } 721 722 float MSG_ReadAngle (void) 723 { 724 return MSG_ReadChar() * (360.0/256); 725 } 726 727 728 729 //=========================================================================== 730 731 void SZ_Alloc (sizebuf_t *buf, int startsize) 732 { 733 if (startsize < 256) 734 startsize = 256; 735 buf->data = (byte*) Hunk_AllocName (startsize, "sizebuf"); 736 buf->maxsize = startsize; 737 buf->cursize = 0; 738 } 739 740 741 void SZ_Free (sizebuf_t *buf) 742 { 743 // Z_Free (buf->data); 744 // buf->data = NULL; 745 // buf->maxsize = 0; 746 buf->cursize = 0; 747 } 748 749 void SZ_Clear (sizebuf_t *buf) 750 { 751 buf->cursize = 0; 752 } 753 754 void *SZ_GetSpace (sizebuf_t *buf, int length) 755 { 756 void *data; 757 758 if (buf->cursize + length > buf->maxsize) 759 { 760 if (!buf->allowoverflow) 761 Sys_Error ("SZ_GetSpace: overflow without allowoverflow set"); 762 763 if (length > buf->maxsize) 764 Sys_Error ("SZ_GetSpace: %i is > full buffer size", length); 765 766 buf->overflowed = true; 767 Con_Printf ("SZ_GetSpace: overflow"); 768 SZ_Clear (buf); 769 } 770 771 data = buf->data + buf->cursize; 772 buf->cursize += length; 773 774 return data; 775 } 776 777 void SZ_Write (sizebuf_t *buf, const void *data, int length) 778 { 779 Q_memcpy (SZ_GetSpace(buf,length),data,length); 780 } 781 782 void SZ_Print (sizebuf_t *buf, const char *data) 783 { 784 int len; 785 786 len = Q_strlen(data)+1; 787 788 // byte * cast to keep VC++ happy 789 if (buf->data[buf->cursize-1]) 790 Q_memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0 791 else 792 Q_memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0 793 } 794 795 796 //============================================================================ 797 798 799 /* 800 ============ 801 COM_SkipPath 802 ============ 803 */ 804 const char *COM_SkipPath (const char *pathname) 805 { 806 const char *last; 807 808 last = pathname; 809 while (*pathname) 810 { 811 if (*pathname=='/') 812 last = pathname+1; 813 pathname++; 814 } 815 return last; 816 } 817 818 /* 819 ============ 820 COM_StripExtension 821 ============ 822 */ 823 void COM_StripExtension (const char *in, char *out) 824 { 825 while (*in && *in != '.') 826 *out++ = *in++; 827 *out = 0; 828 } 829 830 /* 831 ============ 832 COM_FileExtension 833 ============ 834 */ 835 const char *COM_FileExtension (const char *in) 836 { 837 static char exten[8]; 838 int i; 839 840 while (*in && *in != '.') 841 in++; 842 if (!*in) 843 return ""; 844 in++; 845 for (i=0 ; i<7 && *in ; i++,in++) 846 exten[i] = *in; 847 exten[i] = 0; 848 return exten; 849 } 850 851 /* 852 ============ 853 COM_FileBase 854 ============ 855 */ 856 void COM_FileBase (const char *in, char *out, size_t outSize) 857 { 858 // Get the "base" part of a path, make sure we don't exceed outSize bytes 859 860 const char* start; 861 const char* end; 862 size_t len; 863 864 if(!outSize) 865 return; 866 867 start = strrchr(in, '/'); 868 if(start) 869 { 870 start++; 871 } 872 else 873 { 874 start = in; 875 } 876 877 // Start now points to the beginning of the filename part of the file. 878 879 end = strrchr(start, '.'); 880 881 if(!end) 882 { 883 end = start + strlen(start); 884 } 885 886 // end now points one character beyond the end of the base part of the file. 887 888 len = end - start; 889 if(len > outSize - 1) 890 len = outSize - 1; 891 892 memcpy(out, start, len); 893 out[len] = 0; 894 } 895 896 897 /* 898 ================== 899 COM_DefaultExtension 900 ================== 901 */ 902 void COM_DefaultExtension (char *path, const char *extension) 903 { 904 char *src; 905 // 906 // if path doesn't have a .EXT, append extension 907 // (extension should include the .) 908 // 909 src = path + strlen(path) - 1; 910 911 while (*src != '/' && src != path) 912 { 913 if (*src == '.') 914 return; // it has an extension 915 src--; 916 } 917 918 strcat (path, extension); 919 } 920 921 922 /* 923 ============== 924 COM_Parse 925 926 Parse a token out of a string 927 ============== 928 */ 929 char *COM_Parse (char *data) 930 { 931 int c; 932 int len; 933 934 len = 0; 935 com_token[0] = 0; 936 937 if (!data) 938 return NULL; 939 940 // skip whitespace 941 skipwhite: 942 while ( (c = *data) <= ' ') 943 { 944 if (c == 0) 945 return NULL; // end of file; 946 data++; 947 } 948 949 // skip // comments 950 if (c=='/' && data[1] == '/') 951 { 952 while (*data && *data != '\n') 953 data++; 954 goto skipwhite; 955 } 956 957 958 // handle quoted strings specially 959 if (c == '\"') 960 { 961 data++; 962 while (1) 963 { 964 c = *data++; 965 if (c=='\"' || !c) 966 { 967 com_token[len] = 0; 968 return data; 969 } 970 com_token[len] = c; 971 len++; 972 } 973 } 974 975 // parse single characters 976 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') 977 { 978 com_token[len] = c; 979 len++; 980 com_token[len] = 0; 981 return data+1; 982 } 983 984 // parse a regular word 985 do 986 { 987 com_token[len] = c; 988 data++; 989 len++; 990 c = *data; 991 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') 992 break; 993 } while (c>32); 994 995 com_token[len] = 0; 996 return data; 997 } 998 999 1000 /* 1001 ================ 1002 COM_CheckParm 1003 1004 Returns the position (1 to argc-1) in the program's argument list 1005 where the given parameter apears, or 0 if not present 1006 ================ 1007 */ 1008 int COM_CheckParm (const char *parm) 1009 { 1010 int i; 1011 1012 for (i=1 ; i<com_argc ; i++) 1013 { 1014 if (!com_argv[i]) 1015 continue; // NEXTSTEP sometimes clears appkit vars. 1016 if (!Q_strcmp (parm,com_argv[i])) 1017 return i; 1018 } 1019 1020 return 0; 1021 } 1022 1023 /* 1024 ================ 1025 COM_CheckRegistered 1026 1027 Looks for the pop.txt file and verifies it. 1028 Sets the "registered" cvar. 1029 Immediately exits out if an alternate game was attempted to be started without 1030 being registered. 1031 ================ 1032 */ 1033 void COM_CheckRegistered (void) 1034 { 1035 int h; 1036 unsigned short check[128]; 1037 int i; 1038 1039 COM_OpenFile("gfx/pop.lmp", &h); 1040 static_registered = 0; 1041 1042 if (h == -1) 1043 { 1044 #if WINDED 1045 Sys_Error ("This dedicated server requires a full registered copy of Quake"); 1046 #endif 1047 Con_Printf ("Playing shareware version.\n"); 1048 if (com_modified) 1049 Sys_Error ("You must have the registered version to use modified games"); 1050 1051 #ifdef USE_OPENGLES 1052 // For development purposes pretend we're registered. This allows glquake 1053 // file caching to work: 1054 1055 static_registered = 1; 1056 #endif // USE_OPENGLES 1057 return; 1058 } 1059 1060 Sys_FileRead (h, check, sizeof(check)); 1061 COM_CloseFile (h); 1062 1063 for (i=0 ; i<128 ; i++) 1064 if (pop[i] != (unsigned short)BigShort (check[i])) 1065 Sys_Error ("Corrupted data file."); 1066 1067 Cvar_Set ("cmdline", com_cmdline); 1068 Cvar_Set ("registered", "1"); 1069 static_registered = 1; 1070 Con_Printf ("Playing registered version.\n"); 1071 } 1072 1073 1074 void COM_Path_f (void); 1075 1076 1077 /* 1078 ================ 1079 COM_InitArgv 1080 ================ 1081 */ 1082 void COM_InitArgv (int argc, const char **argv) 1083 { 1084 qboolean safe; 1085 int i, j, n; 1086 1087 // reconstitute the command line for the cmdline externally visible cvar 1088 n = 0; 1089 1090 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++) 1091 { 1092 i = 0; 1093 1094 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i]) 1095 { 1096 com_cmdline[n++] = argv[j][i++]; 1097 } 1098 1099 if (n < (CMDLINE_LENGTH - 1)) 1100 com_cmdline[n++] = ' '; 1101 else 1102 break; 1103 } 1104 1105 com_cmdline[n] = 0; 1106 1107 safe = false; 1108 1109 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ; 1110 com_argc++) 1111 { 1112 largv[com_argc] = argv[com_argc]; 1113 if (!Q_strcmp ("-safe", argv[com_argc])) 1114 safe = true; 1115 } 1116 1117 if (safe) 1118 { 1119 // force all the safe-mode switches. Note that we reserved extra space in 1120 // case we need to add these, so we don't need an overflow check 1121 for (i=0 ; i<NUM_SAFE_ARGVS ; i++) 1122 { 1123 largv[com_argc] = safeargvs[i]; 1124 com_argc++; 1125 } 1126 } 1127 1128 largv[com_argc] = argvdummy; 1129 com_argv = largv; 1130 1131 if (COM_CheckParm ("-rogue")) 1132 { 1133 rogue = true; 1134 standard_quake = false; 1135 } 1136 1137 if (COM_CheckParm ("-hipnotic")) 1138 { 1139 hipnotic = true; 1140 standard_quake = false; 1141 } 1142 } 1143 1144 1145 /* 1146 ================ 1147 COM_Init 1148 ================ 1149 */ 1150 1151 typedef union swapTest_ { 1152 byte b[2]; 1153 short s; 1154 } swapTest; 1155 1156 void COM_Init (const char *basedir) 1157 { 1158 swapTest swaptest; 1159 swaptest.b[0] = 1; 1160 swaptest.b[1] = 0; 1161 1162 // set the byte swapping variables in a portable manner 1163 if ( swaptest.s == 1) 1164 { 1165 bigendien = false; 1166 BigShort = ShortSwap; 1167 LittleShort = ShortNoSwap; 1168 BigLong = LongSwap; 1169 LittleLong = LongNoSwap; 1170 BigFloat = FloatSwap; 1171 LittleFloat = FloatNoSwap; 1172 } 1173 else 1174 { 1175 bigendien = true; 1176 BigShort = ShortNoSwap; 1177 LittleShort = ShortSwap; 1178 BigLong = LongNoSwap; 1179 LittleLong = LongSwap; 1180 BigFloat = FloatNoSwap; 1181 LittleFloat = FloatSwap; 1182 } 1183 1184 Cvar_RegisterVariable (®istered); 1185 Cvar_RegisterVariable (&cmdline); 1186 Cmd_AddCommand ("path", COM_Path_f); 1187 1188 COM_InitFilesystem (); 1189 COM_CheckRegistered (); 1190 } 1191 1192 1193 /* 1194 ============ 1195 va 1196 1197 does a varargs printf into a temp buffer, so I don't need to have 1198 varargs versions of all text functions. 1199 FIXME: make this buffer size safe someday 1200 ============ 1201 */ 1202 char *va(const char *format, ...) 1203 { 1204 va_list argptr; 1205 static char string[1024]; 1206 1207 va_start (argptr, format); 1208 vsprintf (string, format,argptr); 1209 va_end (argptr); 1210 1211 return string; 1212 } 1213 1214 1215 /// just for debugging 1216 int memsearch (const byte *start, int count, int search) 1217 { 1218 int i; 1219 1220 for (i=0 ; i<count ; i++) 1221 if (start[i] == search) 1222 return i; 1223 return -1; 1224 } 1225 1226 /* 1227 ============================================================================= 1228 1229 QUAKE FILESYSTEM 1230 1231 ============================================================================= 1232 */ 1233 1234 int com_filesize; 1235 1236 1237 // 1238 // in memory 1239 // 1240 1241 typedef struct 1242 { 1243 char name[MAX_QPATH]; 1244 int filepos, filelen; 1245 } packfile_t; 1246 1247 typedef struct pack_s 1248 { 1249 char filename[MAX_OSPATH]; 1250 int handle; 1251 int numfiles; 1252 packfile_t *files; 1253 } pack_t; 1254 1255 // 1256 // on disk 1257 // 1258 typedef struct 1259 { 1260 char name[56]; 1261 int filepos, filelen; 1262 } dpackfile_t; 1263 1264 typedef struct 1265 { 1266 char id[4]; 1267 int dirofs; 1268 int dirlen; 1269 } dpackheader_t; 1270 1271 #define MAX_FILES_IN_PACK 2048 1272 1273 char com_cachedir[MAX_OSPATH]; 1274 char com_gamedir[MAX_OSPATH]; 1275 1276 typedef struct searchpath_s 1277 { 1278 char filename[MAX_OSPATH]; 1279 pack_t *pack; // only one of filename / pack will be used 1280 struct searchpath_s *next; 1281 } searchpath_t; 1282 1283 searchpath_t *com_searchpaths; 1284 1285 /* 1286 ============ 1287 COM_Path_f 1288 1289 ============ 1290 */ 1291 void COM_Path_f (void) 1292 { 1293 searchpath_t *s; 1294 1295 Con_Printf ("Current search path:\n"); 1296 for (s=com_searchpaths ; s ; s=s->next) 1297 { 1298 if (s->pack) 1299 { 1300 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); 1301 } 1302 else 1303 Con_Printf ("%s\n", s->filename); 1304 } 1305 } 1306 1307 /* 1308 ============ 1309 COM_WriteFile 1310 1311 The filename will be prefixed by the current game directory 1312 ============ 1313 */ 1314 void COM_WriteFile (const char *filename, void *data, int len) 1315 { 1316 int handle; 1317 char name[MAX_OSPATH]; 1318 1319 sprintf (name, "%s/%s", com_gamedir, filename); 1320 1321 handle = Sys_FileOpenWrite (name); 1322 if (handle == -1) 1323 { 1324 Sys_Printf ("COM_WriteFile: failed on %s\n", name); 1325 return; 1326 } 1327 1328 Sys_Printf ("COM_WriteFile: %s\n", name); 1329 Sys_FileWrite (handle, data, len); 1330 Sys_FileClose (handle); 1331 } 1332 1333 1334 /* 1335 ============ 1336 COM_CreatePath 1337 1338 Only used for CopyFile 1339 ============ 1340 */ 1341 void COM_CreatePath (const char *path) 1342 { 1343 char *ofs; 1344 1345 for (ofs = (char*) path+1 ; *ofs ; ofs++) 1346 { 1347 if (*ofs == '/') 1348 { // create the directory 1349 *ofs = 0; 1350 Sys_mkdir (path); 1351 *ofs = '/'; 1352 } 1353 } 1354 } 1355 1356 1357 /* 1358 =========== 1359 COM_CopyFile 1360 1361 Copies a file over from the net to the local cache, creating any directories 1362 needed. This is for the convenience of developers using ISDN from home. 1363 =========== 1364 */ 1365 void COM_CopyFile (const char *netpath, const char *cachepath) 1366 { 1367 int in, out; 1368 int remaining, count; 1369 char buf[4096]; 1370 1371 remaining = Sys_FileOpenRead (netpath, &in); 1372 COM_CreatePath (cachepath); // create directories up to the cache file 1373 out = Sys_FileOpenWrite (cachepath); 1374 1375 while (remaining) 1376 { 1377 if (remaining < (int) sizeof(buf)) 1378 count = remaining; 1379 else 1380 count = sizeof(buf); 1381 Sys_FileRead (in, buf, count); 1382 Sys_FileWrite (out, buf, count); 1383 remaining -= count; 1384 } 1385 1386 Sys_FileClose (in); 1387 Sys_FileClose (out); 1388 } 1389 1390 /* 1391 =========== 1392 COM_FindFile 1393 1394 Finds the file in the search path. 1395 Sets com_filesize and one of handle or file 1396 =========== 1397 */ 1398 int COM_FindFile (const char *filename, int *handle, FILE **file) 1399 { 1400 searchpath_t *search; 1401 char netpath[MAX_OSPATH]; 1402 char cachepath[MAX_OSPATH]; 1403 pack_t *pak; 1404 int i; 1405 int findtime, cachetime; 1406 1407 if (file && handle) 1408 Sys_Error ("COM_FindFile: both handle and file set"); 1409 if (!file && !handle) 1410 Sys_Error ("COM_FindFile: neither handle or file set"); 1411 1412 // 1413 // search through the path, one element at a time 1414 // 1415 search = com_searchpaths; 1416 if (proghack) 1417 { // gross hack to use quake 1 progs with quake 2 maps 1418 if (!strcmp(filename, "progs.dat")) 1419 search = search->next; 1420 } 1421 1422 for ( ; search ; search = search->next) 1423 { 1424 // is the element a pak file? 1425 if (search->pack) 1426 { 1427 // look through all the pak file elements 1428 pak = search->pack; 1429 for (i=0 ; i<pak->numfiles ; i++) 1430 if (!strcmp (pak->files[i].name, filename)) 1431 { // found it! 1432 // Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename); 1433 if (handle) 1434 { 1435 *handle = pak->handle; 1436 Sys_FileSeek (pak->handle, pak->files[i].filepos); 1437 } 1438 else 1439 { // open a new file on the pakfile 1440 *file = fopen (pak->filename, "rb"); 1441 if (*file) 1442 fseek (*file, pak->files[i].filepos, SEEK_SET); 1443 } 1444 com_filesize = pak->files[i].filelen; 1445 return com_filesize; 1446 } 1447 } 1448 else 1449 { 1450 // check a file in the directory tree 1451 if (!static_registered) 1452 { // if not a registered version, don't ever go beyond base 1453 if ( strchr (filename, '/') || strchr (filename,'\\')) 1454 continue; 1455 } 1456 1457 sprintf (netpath, "%s/%s",search->filename, filename); 1458 1459 findtime = Sys_FileTime (netpath); 1460 if (findtime == -1) 1461 continue; 1462 1463 // see if the file needs to be updated in the cache 1464 if (!com_cachedir[0]) 1465 strcpy (cachepath, netpath); 1466 else 1467 { 1468 #if defined(_WIN32) 1469 if ((strlen(netpath) < 2) || (netpath[1] != ':')) 1470 sprintf (cachepath,"%s%s", com_cachedir, netpath); 1471 else 1472 sprintf (cachepath,"%s%s", com_cachedir, netpath+2); 1473 #else 1474 sprintf (cachepath,"%s%s", com_cachedir, netpath); 1475 #endif 1476 1477 cachetime = Sys_FileTime (cachepath); 1478 1479 if (cachetime < findtime) 1480 COM_CopyFile (netpath, cachepath); 1481 strcpy (netpath, cachepath); 1482 } 1483 1484 // Sys_Printf ("FindFile: %s\n",netpath); 1485 com_filesize = Sys_FileOpenRead (netpath, &i); 1486 if (handle) 1487 *handle = i; 1488 else 1489 { 1490 Sys_FileClose (i); 1491 *file = fopen (netpath, "rb"); 1492 } 1493 return com_filesize; 1494 } 1495 1496 } 1497 1498 Sys_Printf ("FindFile: can't find %s\n", filename); 1499 1500 if (handle) 1501 *handle = -1; 1502 else 1503 *file = NULL; 1504 com_filesize = -1; 1505 return -1; 1506 } 1507 1508 1509 /* 1510 =========== 1511 COM_OpenFile 1512 1513 filename never has a leading slash, but may contain directory walks 1514 returns a handle and a length 1515 it may actually be inside a pak file 1516 =========== 1517 */ 1518 int COM_OpenFile (const char *filename, int *handle) 1519 { 1520 return COM_FindFile (filename, handle, NULL); 1521 } 1522 1523 /* 1524 =========== 1525 COM_FOpenFile 1526 1527 If the requested file is inside a packfile, a new FILE * will be opened 1528 into the file. 1529 =========== 1530 */ 1531 int COM_FOpenFile (const char *filename, FILE **file) 1532 { 1533 return COM_FindFile (filename, NULL, file); 1534 } 1535 1536 /* 1537 ============ 1538 COM_CloseFile 1539 1540 If it is a pak file handle, don't really close it 1541 ============ 1542 */ 1543 void COM_CloseFile (int h) 1544 { 1545 searchpath_t *s; 1546 1547 for (s = com_searchpaths ; s ; s=s->next) 1548 if (s->pack && s->pack->handle == h) 1549 return; 1550 1551 Sys_FileClose (h); 1552 } 1553 1554 1555 /* 1556 ============ 1557 COM_LoadFile 1558 1559 Filename are reletive to the quake directory. 1560 Allways appends a 0 byte. 1561 ============ 1562 */ 1563 cache_user_t *loadcache; 1564 byte *loadbuf; 1565 int loadsize; 1566 byte *COM_LoadFile (const char *path, int usehunk) 1567 { 1568 int h; 1569 byte *buf; 1570 char base[32]; 1571 int len; 1572 1573 buf = NULL; // quiet compiler warning 1574 1575 // look for it in the filesystem or pack files 1576 len = COM_OpenFile (path, &h); 1577 if (h == -1) 1578 return NULL; 1579 1580 // extract the filename base name for hunk tag 1581 COM_FileBase (path, base, sizeof(base)); 1582 1583 if (usehunk == 1) 1584 buf = (byte*) Hunk_AllocName (len+1, base); 1585 else if (usehunk == 2) 1586 buf = (byte*) Hunk_TempAlloc (len+1); 1587 else if (usehunk == 0) 1588 buf = (byte*) Z_Malloc (len+1); 1589 else if (usehunk == 3) 1590 buf = (byte*) Cache_Alloc (loadcache, len+1, base); 1591 else if (usehunk == 4) 1592 { 1593 if (len+1 > loadsize) 1594 buf = (byte*) Hunk_TempAlloc (len+1); 1595 else 1596 buf = loadbuf; 1597 } 1598 else 1599 Sys_Error ("COM_LoadFile: bad usehunk"); 1600 1601 if (!buf) 1602 Sys_Error ("COM_LoadFile: not enough space for %s", path); 1603 1604 ((byte *)buf)[len] = 0; 1605 1606 Draw_BeginDisc (); 1607 Sys_FileRead (h, buf, len); 1608 COM_CloseFile (h); 1609 Draw_EndDisc (); 1610 1611 return buf; 1612 } 1613 1614 byte *COM_LoadHunkFile (const char *path) 1615 { 1616 return COM_LoadFile (path, 1); 1617 } 1618 1619 byte *COM_LoadTempFile (const char *path) 1620 { 1621 return COM_LoadFile (path, 2); 1622 } 1623 1624 void COM_LoadCacheFile (char *path, struct cache_user_s *cu) 1625 { 1626 loadcache = cu; 1627 COM_LoadFile (path, 3); 1628 } 1629 1630 // uses temp hunk if larger than bufsize 1631 byte *COM_LoadStackFile (const char *path, void *buffer, int bufsize) 1632 { 1633 byte *buf; 1634 1635 loadbuf = (byte *)buffer; 1636 loadsize = bufsize; 1637 buf = COM_LoadFile (path, 4); 1638 1639 return buf; 1640 } 1641 1642 /* 1643 ================= 1644 COM_LoadPackFile 1645 1646 Takes an explicit (not game tree related) path to a pak file. 1647 1648 Loads the header and directory, adding the files at the beginning 1649 of the list so they override previous pack files. 1650 ================= 1651 */ 1652 pack_t *COM_LoadPackFile (const char *packfile) 1653 { 1654 dpackheader_t header; 1655 int i; 1656 packfile_t *newfiles; 1657 int numpackfiles; 1658 pack_t *pack; 1659 int packhandle; 1660 dpackfile_t info[MAX_FILES_IN_PACK]; 1661 unsigned short crc; 1662 1663 if (Sys_FileOpenRead (packfile, &packhandle) == -1) 1664 { 1665 // Con_Printf ("Couldn't open %s\n", packfile); 1666 return NULL; 1667 } 1668 Sys_FileRead (packhandle, (void *)&header, sizeof(header)); 1669 if (header.id[0] != 'P' || header.id[1] != 'A' 1670 || header.id[2] != 'C' || header.id[3] != 'K') 1671 Sys_Error ("%s is not a packfile", packfile); 1672 header.dirofs = LittleLong (header.dirofs); 1673 header.dirlen = LittleLong (header.dirlen); 1674 1675 numpackfiles = header.dirlen / sizeof(dpackfile_t); 1676 1677 if (numpackfiles > MAX_FILES_IN_PACK) 1678 Sys_Error ("%s has %i files", packfile, numpackfiles); 1679 1680 if (numpackfiles != PAK0_COUNT) 1681 com_modified = true; // not the original file 1682 1683 newfiles = (packfile_t*) Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile"); 1684 1685 Sys_FileSeek (packhandle, header.dirofs); 1686 Sys_FileRead (packhandle, (void *)info, header.dirlen); 1687 1688 // crc the directory to check for modifications 1689 CRC_Init (&crc); 1690 for (i=0 ; i<header.dirlen ; i++) 1691 CRC_ProcessByte (&crc, ((byte *)info)[i]); 1692 if (crc != PAK0_CRC) 1693 com_modified = true; 1694 1695 // parse the directory 1696 for (i=0 ; i<numpackfiles ; i++) 1697 { 1698 strcpy (newfiles[i].name, info[i].name); 1699 newfiles[i].filepos = LittleLong(info[i].filepos); 1700 newfiles[i].filelen = LittleLong(info[i].filelen); 1701 } 1702 1703 pack = (pack_t*) Hunk_Alloc (sizeof (pack_t)); 1704 strcpy (pack->filename, packfile); 1705 pack->handle = packhandle; 1706 pack->numfiles = numpackfiles; 1707 pack->files = newfiles; 1708 1709 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); 1710 return pack; 1711 } 1712 1713 1714 /* 1715 ================ 1716 COM_AddGameDirectory 1717 1718 Sets com_gamedir, adds the directory to the head of the path, 1719 then loads and adds pak1.pak pak2.pak ... 1720 ================ 1721 */ 1722 void COM_AddGameDirectory (char *dir) 1723 { 1724 int i; 1725 searchpath_t *search; 1726 pack_t *pak; 1727 char pakfile[MAX_OSPATH]; 1728 1729 strcpy (com_gamedir, dir); 1730 1731 // 1732 // add the directory to the search path 1733 // 1734 search = (searchpath_t*) Hunk_Alloc (sizeof(searchpath_t)); 1735 strcpy (search->filename, dir); 1736 search->next = com_searchpaths; 1737 com_searchpaths = search; 1738 1739 // 1740 // add any pak files in the format pak0.pak pak1.pak, ... 1741 // 1742 for (i=0 ; ; i++) 1743 { 1744 sprintf (pakfile, "%s/pak%i.pak", dir, i); 1745 pak = COM_LoadPackFile (pakfile); 1746 if (!pak) 1747 break; 1748 search = (searchpath_t*) Hunk_Alloc (sizeof(searchpath_t)); 1749 search->pack = pak; 1750 search->next = com_searchpaths; 1751 com_searchpaths = search; 1752 } 1753 1754 // 1755 // add the contents of the parms.txt file to the end of the command line 1756 // 1757 1758 } 1759 1760 /* 1761 ================ 1762 COM_InitFilesystem 1763 ================ 1764 */ 1765 void COM_InitFilesystem (void) 1766 { 1767 int i, j; 1768 char basedir[MAX_OSPATH]; 1769 searchpath_t *search; 1770 1771 // 1772 // -basedir <path> 1773 // Overrides the system supplied base directory (under GAMENAME) 1774 // 1775 i = COM_CheckParm ("-basedir"); 1776 if (i && i < com_argc-1) 1777 strcpy (basedir, com_argv[i+1]); 1778 else 1779 strcpy (basedir, host_parms.basedir); 1780 1781 j = strlen (basedir); 1782 1783 if (j > 0) 1784 { 1785 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/')) 1786 basedir[j-1] = 0; 1787 } 1788 1789 // 1790 // -cachedir <path> 1791 // Overrides the system supplied cache directory (NULL or /qcache) 1792 // -cachedir - will disable caching. 1793 // 1794 i = COM_CheckParm ("-cachedir"); 1795 if (i && i < com_argc-1) 1796 { 1797 if (com_argv[i+1][0] == '-') 1798 com_cachedir[0] = 0; 1799 else 1800 strcpy (com_cachedir, com_argv[i+1]); 1801 } 1802 else if (host_parms.cachedir) 1803 strcpy (com_cachedir, host_parms.cachedir); 1804 else 1805 com_cachedir[0] = 0; 1806 1807 // 1808 // start up with GAMENAME by default (id1) 1809 // 1810 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) ); 1811 1812 if (COM_CheckParm ("-rogue")) 1813 COM_AddGameDirectory (va("%s/rogue", basedir) ); 1814 if (COM_CheckParm ("-hipnotic")) 1815 COM_AddGameDirectory (va("%s/hipnotic", basedir) ); 1816 1817 // 1818 // -game <gamedir> 1819 // Adds basedir/gamedir as an override game 1820 // 1821 i = COM_CheckParm ("-game"); 1822 if (i && i < com_argc-1) 1823 { 1824 com_modified = true; 1825 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1])); 1826 } 1827 1828 // 1829 // -path <dir or packfile> [<dir or packfile>] ... 1830 // Fully specifies the exact serach path, overriding the generated one 1831 // 1832 i = COM_CheckParm ("-path"); 1833 if (i) 1834 { 1835 com_modified = true; 1836 com_searchpaths = NULL; 1837 while (++i < com_argc) 1838 { 1839 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-') 1840 break; 1841 1842 search = (searchpath_t*) Hunk_Alloc (sizeof(searchpath_t)); 1843 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") ) 1844 { 1845 search->pack = COM_LoadPackFile (com_argv[i]); 1846 if (!search->pack) 1847 Sys_Error ("Couldn't load packfile: %s", com_argv[i]); 1848 } 1849 else 1850 strcpy (search->filename, com_argv[i]); 1851 search->next = com_searchpaths; 1852 com_searchpaths = search; 1853 } 1854 } 1855 1856 if (COM_CheckParm ("-proghack")) 1857 proghack = true; 1858 } 1859 1860 1861