QE3.CPP (14574B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 #include "stdafx.h" 23 #include "qe3.h" 24 #include "PrefsDlg.h" 25 #include <direct.h> 26 #include <sys\stat.h> 27 28 QEGlobals_t g_qeglobals; 29 30 void WINAPI QE_CheckOpenGLForErrors(void) 31 { 32 CString strMsg; 33 int i = qglGetError(); 34 if (i != GL_NO_ERROR) 35 { 36 if (i == GL_OUT_OF_MEMORY) 37 { 38 //strMsg.Format("OpenGL out of memory error %s\nDo you wish to save before exiting?", qgluErrorString((GLenum)i)); 39 if (MessageBox(g_qeglobals.d_hwndMain, strMsg, "Q3Radiant Error", MB_YESNO) == IDYES) 40 { 41 Map_SaveFile(NULL, false); 42 } 43 exit(1); 44 } 45 else 46 { 47 //strMsg.Format("Warning: OpenGL Error %s\n ", qgluErrorString((GLenum)i)); 48 Sys_Printf (strMsg.GetBuffer(0)); 49 } 50 } 51 } 52 53 54 char *ExpandReletivePath (char *p) 55 { 56 static char temp[1024]; 57 char *base; 58 59 if (!p || !p[0]) 60 return NULL; 61 if (p[0] == '/' || p[0] == '\\') 62 return p; 63 64 base = ValueForKey(g_qeglobals.d_project_entity, "basepath"); 65 sprintf (temp, "%s/%s", base, p); 66 return temp; 67 } 68 69 char *copystring (char *s) 70 { 71 char *b; 72 b = (char*)malloc(strlen(s)+1); 73 strcpy (b,s); 74 return b; 75 } 76 77 78 bool DoesFileExist(const char* pBuff, long& lSize) 79 { 80 CFile file; 81 if (file.Open(pBuff, CFile::modeRead | CFile::shareDenyNone)) 82 { 83 lSize += file.GetLength(); 84 file.Close(); 85 return true; 86 } 87 return false; 88 } 89 90 91 void Map_Snapshot() 92 { 93 CString strMsg; 94 // we need to do the following 95 // 1. make sure the snapshot directory exists (create it if it doesn't) 96 // 2. find out what the lastest save is based on number 97 // 3. inc that and save the map 98 CString strOrgPath, strOrgFile; 99 ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile); 100 AddSlash(strOrgPath); 101 strOrgPath += "snapshots"; 102 bool bGo = true; 103 struct _stat Stat; 104 if (_stat(strOrgPath, &Stat) == -1) 105 { 106 bGo = (_mkdir(strOrgPath) != -1); 107 } 108 AddSlash(strOrgPath); 109 if (bGo) 110 { 111 int nCount = 0; 112 long lSize = 0; 113 CString strNewPath = strOrgPath; 114 strNewPath += strOrgFile; 115 CString strFile; 116 while (bGo) 117 { 118 strFile.Format("%s.%i", strNewPath, nCount); 119 bGo = DoesFileExist(strFile, lSize); 120 nCount++; 121 } 122 // strFile has the next available slot 123 Map_SaveFile(strFile.GetBuffer(0), false); 124 Sys_SetTitle (currentmap); 125 if (lSize > 12 * 1024 * 1024) // total size of saves > 4 mb 126 { 127 Sys_Printf("The snapshot files in the [%s] directory total more than 4 megabytes. You might consider cleaning the directory up.", strOrgPath); 128 } 129 } 130 else 131 { 132 strMsg.Format("Snapshot save failed.. unabled to create directory\n%s", strOrgPath); 133 g_pParentWnd->MessageBox(strMsg); 134 } 135 } 136 /* 137 =============== 138 QE_CheckAutoSave 139 140 If five minutes have passed since making a change 141 and the map hasn't been saved, save it out. 142 =============== 143 */ 144 145 146 void QE_CheckAutoSave( void ) 147 { 148 static clock_t s_start; 149 clock_t now; 150 151 now = clock(); 152 153 if ( modified != 1 || !s_start) 154 { 155 s_start = now; 156 return; 157 } 158 159 if ( now - s_start > ( CLOCKS_PER_SEC * 60 * g_PrefsDlg.m_nAutoSave)) 160 { 161 162 if (g_PrefsDlg.m_bAutoSave) 163 { 164 CString strMsg = g_PrefsDlg.m_bSnapShots ? "Autosaving snapshot..." : "Autosaving..."; 165 Sys_Printf(strMsg.GetBuffer(0)); 166 Sys_Printf("\n"); 167 Sys_Status (strMsg.GetBuffer(0),0); 168 169 // only snapshot if not working on a default map 170 if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0) 171 { 172 Map_Snapshot(); 173 } 174 else 175 { 176 Map_SaveFile (ValueForKey(g_qeglobals.d_project_entity, "autosave"), false); 177 } 178 179 Sys_Status ("Autosaving...Saved.", 0 ); 180 modified = 2; 181 } 182 else 183 { 184 Sys_Printf ("Autosave skipped...\n"); 185 Sys_Status ("Autosave skipped...", 0 ); 186 } 187 s_start = now; 188 } 189 } 190 191 192 int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen) 193 { 194 char *pFile = NULL; 195 int nResult = GetFullPathName(pPath, nBufferLen, pBuffer, &pFile); 196 nResult = GetShortPathName(pPath, pBuffer, nBufferLen); 197 if (nResult == 0) 198 strcpy(pBuffer, pPath); // Use long filename 199 return nResult; 200 } 201 202 203 204 const char *g_pPathFixups[]= 205 { 206 "basepath", 207 "remotebasepath", 208 "entitypath", 209 "texturepath", 210 "autosave", 211 "mapspath" 212 }; 213 214 const int g_nPathFixupCount = sizeof(g_pPathFixups) / sizeof(const char*); 215 216 /* 217 =========== 218 QE_LoadProject 219 =========== 220 */ 221 qboolean QE_LoadProject (char *projectfile) 222 { 223 char *data; 224 225 Sys_Printf ("QE_LoadProject (%s)\n", projectfile); 226 227 if ( LoadFileNoCrash (projectfile, (void **)&data) == -1) 228 return false; 229 230 g_strProject = projectfile; 231 232 CString strData = data; 233 free(data); 234 235 CString strQ2Path = g_PrefsDlg.m_strQuake2; 236 CString strQ2File; 237 ExtractPath_and_Filename(g_PrefsDlg.m_strQuake2, strQ2Path, strQ2File); 238 AddSlash(strQ2Path); 239 240 241 char* pBuff = new char[1024]; 242 243 BuildShortPathName(strQ2Path, pBuff, 1024); 244 FindReplace(strData, "__Q2PATH", pBuff); 245 BuildShortPathName(g_strAppPath, pBuff, 1024); 246 FindReplace(strData, "__QERPATH", pBuff); 247 248 char* pFile; 249 if (GetFullPathName(projectfile, 1024, pBuff, &pFile)) 250 { 251 g_PrefsDlg.m_strLastProject = pBuff; 252 BuildShortPathName(g_PrefsDlg.m_strLastProject, pBuff, 1024); 253 g_PrefsDlg.m_strLastProject = pBuff; 254 g_PrefsDlg.SavePrefs(); 255 256 ExtractPath_and_Filename(pBuff, strQ2Path, strQ2File); 257 int nLen = strQ2Path.GetLength(); 258 if (nLen > 0) 259 { 260 if (strQ2Path[nLen - 1] == '\\') 261 strQ2Path.SetAt(nLen-1,'\0'); 262 char* pBuffer = strQ2Path.GetBufferSetLength(_MAX_PATH + 1); 263 int n = strQ2Path.ReverseFind('\\'); 264 if (n >=0 ) 265 pBuffer[n + 1] = '\0'; 266 strQ2Path.ReleaseBuffer(); 267 FindReplace(strData, "__QEPROJPATH", strQ2Path); 268 } 269 } 270 271 272 StartTokenParsing (strData.GetBuffer(0)); 273 g_qeglobals.d_project_entity = Entity_Parse (true); 274 if (!g_qeglobals.d_project_entity) 275 Error ("Couldn't parse %s", projectfile); 276 277 for (int i = 0; i < g_nPathFixupCount; i++) 278 { 279 char *pPath = ValueForKey (g_qeglobals.d_project_entity, g_pPathFixups[i]); 280 if (pPath[0] != '\\' && pPath[0] != '/') 281 { 282 if (GetFullPathName(pPath, 1024, pBuff, &pFile)) 283 { 284 SetKeyValue(g_qeglobals.d_project_entity, g_pPathFixups[i], pBuff); 285 } 286 } 287 } 288 289 delete []pBuff; 290 291 // set here some default project settings you need 292 if ( strlen( ValueForKey( g_qeglobals.d_project_entity, "brush_primit" ) ) == 0 ) 293 { 294 SetKeyValue( g_qeglobals.d_project_entity, "brush_primit", "0" ); 295 } 296 297 g_qeglobals.m_bBrushPrimitMode = IntForKey( g_qeglobals.d_project_entity, "brush_primit" ); 298 299 300 Eclass_InitForSourceDirectory (ValueForKey (g_qeglobals.d_project_entity, "entitypath")); 301 FillClassList(); // list in entity window 302 303 Map_New(); 304 305 306 FillTextureMenu(); 307 FillBSPMenu(); 308 309 return true; 310 } 311 312 /* 313 =========== 314 QE_SaveProject 315 =========== 316 */ 317 //extern char *bsp_commands[256]; 318 319 qboolean QE_SaveProject (const char* pProjectFile) 320 { 321 //char filename[1024]; 322 FILE *fp; 323 epair_t *ep; 324 325 //sprintf (filename, "%s\\%s.prj", g_projectdir, g_username); 326 327 if (!(fp = fopen (pProjectFile, "w+"))) 328 Error ("Could not open project file!"); 329 330 fprintf (fp, "{\n"); 331 for (ep = g_qeglobals.d_project_entity->epairs; ep; ep=ep->next) 332 fprintf (fp, "\"%s\" \"%s\"\n", ep->key, ep->value); 333 fprintf (fp, "}\n"); 334 335 fclose (fp); 336 337 return TRUE; 338 } 339 340 341 342 /* 343 =========== 344 QE_KeyDown 345 =========== 346 */ 347 #define SPEED_MOVE 32 348 #define SPEED_TURN 22.5 349 350 351 /* 352 =============== 353 ConnectEntities 354 355 Sets target / targetname on the two entities selected 356 from the first selected to the secon 357 =============== 358 */ 359 void ConnectEntities (void) 360 { 361 entity_t *e1, *e2, *e; 362 char *target, *tn; 363 int maxtarg, targetnum; 364 char newtarg[32]; 365 366 if (g_qeglobals.d_select_count != 2) 367 { 368 Sys_Status ("Must have two brushes selected.", 0); 369 Sys_Beep (); 370 return; 371 } 372 373 e1 = g_qeglobals.d_select_order[0]->owner; 374 e2 = g_qeglobals.d_select_order[1]->owner; 375 376 if (e1 == world_entity || e2 == world_entity) 377 { 378 Sys_Status ("Can't connect to the world.", 0); 379 Sys_Beep (); 380 return; 381 } 382 383 if (e1 == e2) 384 { 385 Sys_Status ("Brushes are from same entity.", 0); 386 Sys_Beep (); 387 return; 388 } 389 390 target = ValueForKey (e1, "target"); 391 if (target && target[0]) 392 strcpy (newtarg, target); 393 else 394 { 395 target = ValueForKey (e2, "targetname"); 396 if (target && target[0]) 397 strcpy (newtarg, target); 398 else 399 { 400 // make a unique target value 401 maxtarg = 0; 402 for (e=entities.next ; e != &entities ; e=e->next) 403 { 404 tn = ValueForKey (e, "targetname"); 405 if (tn && tn[0]) 406 { 407 targetnum = atoi(tn+1); 408 if (targetnum > maxtarg) 409 maxtarg = targetnum; 410 } 411 } 412 sprintf (newtarg, "t%i", maxtarg+1); 413 } 414 } 415 416 SetKeyValue (e1, "target", newtarg); 417 SetKeyValue (e2, "targetname", newtarg); 418 Sys_UpdateWindows (W_XY | W_CAMERA); 419 420 Select_Deselect(); 421 Select_Brush (g_qeglobals.d_select_order[1]); 422 } 423 424 qboolean QE_SingleBrush (bool bQuiet) 425 { 426 if ( (selected_brushes.next == &selected_brushes) 427 || (selected_brushes.next->next != &selected_brushes) ) 428 { 429 if (!bQuiet) 430 { 431 Sys_Printf ("Error: you must have a single brush selected\n"); 432 } 433 return false; 434 } 435 if (selected_brushes.next->owner->eclass->fixedsize) 436 { 437 if (!bQuiet) 438 { 439 Sys_Printf ("Error: you cannot manipulate fixed size entities\n"); 440 } 441 return false; 442 } 443 444 return true; 445 } 446 447 void QE_Init (void) 448 { 449 /* 450 ** initialize variables 451 */ 452 g_qeglobals.d_gridsize = 8; 453 g_qeglobals.d_showgrid = true; 454 455 /* 456 ** other stuff 457 */ 458 Texture_Init (true); 459 //Cam_Init (); 460 //XY_Init (); 461 Z_Init (); 462 Terrain_Init(); 463 } 464 465 void WINAPI QE_ConvertDOSToUnixName( char *dst, const char *src ) 466 { 467 while ( *src ) 468 { 469 if ( *src == '\\' ) 470 *dst = '/'; 471 else 472 *dst = *src; 473 dst++; src++; 474 } 475 *dst = 0; 476 } 477 478 int g_numbrushes, g_numentities; 479 480 void QE_CountBrushesAndUpdateStatusBar( void ) 481 { 482 static int s_lastbrushcount, s_lastentitycount; 483 static qboolean s_didonce; 484 485 //entity_t *e; 486 brush_t *b, *next; 487 488 g_numbrushes = 0; 489 g_numentities = 0; 490 491 if ( active_brushes.next != NULL ) 492 { 493 for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next) 494 { 495 next = b->next; 496 if (b->brush_faces ) 497 { 498 if ( !b->owner->eclass->fixedsize) 499 g_numbrushes++; 500 else 501 g_numentities++; 502 } 503 } 504 } 505 /* 506 if ( entities.next != NULL ) 507 { 508 for ( e = entities.next ; e != &entities && g_numentities != MAX_MAP_ENTITIES ; e = e->next) 509 { 510 g_numentities++; 511 } 512 } 513 */ 514 if ( ( ( g_numbrushes != s_lastbrushcount ) || ( g_numentities != s_lastentitycount ) ) || ( !s_didonce ) ) 515 { 516 Sys_UpdateStatusBar(); 517 518 s_lastbrushcount = g_numbrushes; 519 s_lastentitycount = g_numentities; 520 s_didonce = true; 521 } 522 } 523 524 char com_token[1024]; 525 qboolean com_eof; 526 527 /* 528 ================ 529 I_FloatTime 530 ================ 531 */ 532 double I_FloatTime (void) 533 { 534 time_t t; 535 536 time (&t); 537 538 return t; 539 #if 0 540 // more precise, less portable 541 struct timeval tp; 542 struct timezone tzp; 543 static int secbase; 544 545 gettimeofday(&tp, &tzp); 546 547 if (!secbase) 548 { 549 secbase = tp.tv_sec; 550 return tp.tv_usec/1000000.0; 551 } 552 553 return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; 554 #endif 555 } 556 557 558 /* 559 ============== 560 COM_Parse 561 562 Parse a token out of a string 563 ============== 564 */ 565 char *COM_Parse (char *data) 566 { 567 int c; 568 int len; 569 570 len = 0; 571 com_token[0] = 0; 572 573 if (!data) 574 return NULL; 575 576 // skip whitespace 577 skipwhite: 578 while ( (c = *data) <= ' ') 579 { 580 if (c == 0) 581 { 582 com_eof = true; 583 return NULL; // end of file; 584 } 585 data++; 586 } 587 588 // skip // comments 589 if (c=='/' && data[1] == '/') 590 { 591 while (*data && *data != '\n') 592 data++; 593 goto skipwhite; 594 } 595 596 597 // handle quoted strings specially 598 if (c == '\"') 599 { 600 data++; 601 do 602 { 603 c = *data++; 604 if (c=='\"') 605 { 606 com_token[len] = 0; 607 return data; 608 } 609 com_token[len] = c; 610 len++; 611 } while (1); 612 } 613 614 // parse single characters 615 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') 616 { 617 com_token[len] = c; 618 len++; 619 com_token[len] = 0; 620 return data+1; 621 } 622 623 // parse a regular word 624 do 625 { 626 com_token[len] = c; 627 data++; 628 len++; 629 c = *data; 630 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') 631 break; 632 } while (c>32); 633 634 com_token[len] = 0; 635 return data; 636 } 637 638 639 640 /* 641 ============================================================================= 642 643 MISC FUNCTIONS 644 645 ============================================================================= 646 */ 647 648 649 int argc; 650 char *argv[MAX_NUM_ARGVS]; 651 652 /* 653 ============ 654 ParseCommandLine 655 ============ 656 */ 657 void ParseCommandLine (char *lpCmdLine) 658 { 659 argc = 1; 660 argv[0] = "programname"; 661 662 while (*lpCmdLine && (argc < MAX_NUM_ARGVS)) 663 { 664 while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) 665 lpCmdLine++; 666 667 if (*lpCmdLine) 668 { 669 argv[argc] = lpCmdLine; 670 argc++; 671 672 while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) 673 lpCmdLine++; 674 675 if (*lpCmdLine) 676 { 677 *lpCmdLine = 0; 678 lpCmdLine++; 679 } 680 681 } 682 } 683 } 684 685 686 687 /* 688 ================= 689 CheckParm 690 691 Checks for the given parameter in the program's command line arguments 692 Returns the argument number (1 to argc-1) or 0 if not present 693 ================= 694 */ 695 int CheckParm (char *check) 696 { 697 int i; 698 699 for (i = 1;i<argc;i++) 700 { 701 if ( stricmp(check, argv[i]) ) 702 return i; 703 } 704 705 return 0; 706 } 707 708 709 710 711 /* 712 ============== 713 ParseNum / ParseHex 714 ============== 715 */ 716 int ParseHex (char *hex) 717 { 718 char *str; 719 int num; 720 721 num = 0; 722 str = hex; 723 724 while (*str) 725 { 726 num <<= 4; 727 if (*str >= '0' && *str <= '9') 728 num += *str-'0'; 729 else if (*str >= 'a' && *str <= 'f') 730 num += 10 + *str-'a'; 731 else if (*str >= 'A' && *str <= 'F') 732 num += 10 + *str-'A'; 733 else 734 Error ("Bad hex number: %s",hex); 735 str++; 736 } 737 738 return num; 739 } 740 741 742 int ParseNum (char *str) 743 { 744 if (str[0] == '$') 745 return ParseHex (str+1); 746 if (str[0] == '0' && str[1] == 'x') 747 return ParseHex (str+2); 748 return atol (str); 749 } 750 751