l_qfiles.c (17763B)
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 23 #if defined(WIN32)|defined(_WIN32) 24 #include <windows.h> 25 #include <sys/types.h> 26 #include <sys/stat.h> 27 #else 28 #include <glob.h> 29 #include <sys/stat.h> 30 #include <unistd.h> 31 #endif 32 33 #include "qbsp.h" 34 35 //file extensions with their type 36 typedef struct qfile_exttype_s 37 { 38 char *extension; 39 int type; 40 } qfile_exttyp_t; 41 42 qfile_exttyp_t quakefiletypes[] = 43 { 44 {QFILEEXT_UNKNOWN, QFILETYPE_UNKNOWN}, 45 {QFILEEXT_PAK, QFILETYPE_PAK}, 46 {QFILEEXT_PK3, QFILETYPE_PK3}, 47 {QFILEEXT_SIN, QFILETYPE_PAK}, 48 {QFILEEXT_BSP, QFILETYPE_BSP}, 49 {QFILEEXT_MAP, QFILETYPE_MAP}, 50 {QFILEEXT_MDL, QFILETYPE_MDL}, 51 {QFILEEXT_MD2, QFILETYPE_MD2}, 52 {QFILEEXT_MD3, QFILETYPE_MD3}, 53 {QFILEEXT_WAL, QFILETYPE_WAL}, 54 {QFILEEXT_WAV, QFILETYPE_WAV}, 55 {QFILEEXT_AAS, QFILETYPE_AAS}, 56 {NULL, 0} 57 }; 58 59 //=========================================================================== 60 // 61 // Parameter: - 62 // Returns: - 63 // Changes Globals: - 64 //=========================================================================== 65 int QuakeFileExtensionType(char *extension) 66 { 67 int i; 68 69 for (i = 0; quakefiletypes[i].extension; i++) 70 { 71 if (!stricmp(extension, quakefiletypes[i].extension)) 72 { 73 return quakefiletypes[i].type; 74 } //end if 75 } //end for 76 return QFILETYPE_UNKNOWN; 77 } //end of the function QuakeFileExtensionType 78 //=========================================================================== 79 // 80 // Parameter: - 81 // Returns: - 82 // Changes Globals: - 83 //=========================================================================== 84 char *QuakeFileTypeExtension(int type) 85 { 86 int i; 87 88 for (i = 0; quakefiletypes[i].extension; i++) 89 { 90 if (quakefiletypes[i].type == type) 91 { 92 return quakefiletypes[i].extension; 93 } //end if 94 } //end for 95 return QFILEEXT_UNKNOWN; 96 } //end of the function QuakeFileExtension 97 //=========================================================================== 98 // 99 // Parameter: - 100 // Returns: - 101 // Changes Globals: - 102 //=========================================================================== 103 int QuakeFileType(char *filename) 104 { 105 char ext[_MAX_PATH] = "."; 106 107 ExtractFileExtension(filename, ext+1); 108 return QuakeFileExtensionType(ext); 109 } //end of the function QuakeFileTypeFromFileName 110 //=========================================================================== 111 // 112 // Parameter: - 113 // Returns: - 114 // Changes Globals: - 115 //=========================================================================== 116 char *StringContains(char *str1, char *str2, int casesensitive) 117 { 118 int len, i, j; 119 120 len = strlen(str1) - strlen(str2); 121 for (i = 0; i <= len; i++, str1++) 122 { 123 for (j = 0; str2[j]; j++) 124 { 125 if (casesensitive) 126 { 127 if (str1[j] != str2[j]) break; 128 } //end if 129 else 130 { 131 if (toupper(str1[j]) != toupper(str2[j])) break; 132 } //end else 133 } //end for 134 if (!str2[j]) return str1; 135 } //end for 136 return NULL; 137 } //end of the function StringContains 138 //=========================================================================== 139 // 140 // Parameter: - 141 // Returns: - 142 // Changes Globals: - 143 //=========================================================================== 144 int FileFilter(char *filter, char *filename, int casesensitive) 145 { 146 char buf[1024]; 147 char *ptr; 148 int i, found; 149 150 while(*filter) 151 { 152 if (*filter == '*') 153 { 154 filter++; 155 for (i = 0; *filter; i++) 156 { 157 if (*filter == '*' || *filter == '?') break; 158 buf[i] = *filter; 159 filter++; 160 } //end for 161 buf[i] = '\0'; 162 if (strlen(buf)) 163 { 164 ptr = StringContains(filename, buf, casesensitive); 165 if (!ptr) return false; 166 filename = ptr + strlen(buf); 167 } //end if 168 } //end if 169 else if (*filter == '?') 170 { 171 filter++; 172 filename++; 173 } //end else if 174 else if (*filter == '[' && *(filter+1) == '[') 175 { 176 filter++; 177 } //end if 178 else if (*filter == '[') 179 { 180 filter++; 181 found = false; 182 while(*filter && !found) 183 { 184 if (*filter == ']' && *(filter+1) != ']') break; 185 if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) 186 { 187 if (casesensitive) 188 { 189 if (*filename >= *filter && *filename <= *(filter+2)) found = true; 190 } //end if 191 else 192 { 193 if (toupper(*filename) >= toupper(*filter) && 194 toupper(*filename) <= toupper(*(filter+2))) found = true; 195 } //end else 196 filter += 3; 197 } //end if 198 else 199 { 200 if (casesensitive) 201 { 202 if (*filter == *filename) found = true; 203 } //end if 204 else 205 { 206 if (toupper(*filter) == toupper(*filename)) found = true; 207 } //end else 208 filter++; 209 } //end else 210 } //end while 211 if (!found) return false; 212 while(*filter) 213 { 214 if (*filter == ']' && *(filter+1) != ']') break; 215 filter++; 216 } //end while 217 filter++; 218 filename++; 219 } //end else if 220 else 221 { 222 if (casesensitive) 223 { 224 if (*filter != *filename) return false; 225 } //end if 226 else 227 { 228 if (toupper(*filter) != toupper(*filename)) return false; 229 } //end else 230 filter++; 231 filename++; 232 } //end else 233 } //end while 234 return true; 235 } //end of the function FileFilter 236 //=========================================================================== 237 // 238 // Parameter: - 239 // Returns: - 240 // Changes Globals: - 241 //=========================================================================== 242 quakefile_t *FindQuakeFilesInZip(char *zipfile, char *filter) 243 { 244 unzFile uf; 245 int err; 246 unz_global_info gi; 247 char filename_inzip[MAX_PATH]; 248 unz_file_info file_info; 249 int i; 250 quakefile_t *qfiles, *lastqf, *qf; 251 252 uf = unzOpen(zipfile); 253 err = unzGetGlobalInfo(uf, &gi); 254 255 if (err != UNZ_OK) return NULL; 256 257 unzGoToFirstFile(uf); 258 259 qfiles = NULL; 260 lastqf = NULL; 261 for (i = 0; i < gi.number_entry; i++) 262 { 263 err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL,0,NULL,0); 264 if (err != UNZ_OK) break; 265 266 ConvertPath(filename_inzip); 267 if (FileFilter(filter, filename_inzip, false)) 268 { 269 qf = malloc(sizeof(quakefile_t)); 270 if (!qf) Error("out of memory"); 271 memset(qf, 0, sizeof(quakefile_t)); 272 strcpy(qf->pakfile, zipfile); 273 strcpy(qf->filename, zipfile); 274 strcpy(qf->origname, filename_inzip); 275 qf->zipfile = true; 276 //memcpy( &buildBuffer[i].zipfileinfo, (unz_s*)uf, sizeof(unz_s)); 277 memcpy(&qf->zipinfo, (unz_s*)uf, sizeof(unz_s)); 278 qf->offset = 0; 279 qf->length = file_info.uncompressed_size; 280 qf->type = QuakeFileType(filename_inzip); 281 //add the file ot the list 282 qf->next = NULL; 283 if (lastqf) lastqf->next = qf; 284 else qfiles = qf; 285 lastqf = qf; 286 } //end if 287 unzGoToNextFile(uf); 288 } //end for 289 290 unzClose(uf); 291 292 return qfiles; 293 } //end of the function FindQuakeFilesInZip 294 //=========================================================================== 295 // 296 // Parameter: - 297 // Returns: - 298 // Changes Globals: - 299 //=========================================================================== 300 quakefile_t *FindQuakeFilesInPak(char *pakfile, char *filter) 301 { 302 FILE *fp; 303 dpackheader_t packheader; 304 dsinpackfile_t *packfiles; 305 dpackfile_t *idpackfiles; 306 quakefile_t *qfiles, *lastqf, *qf; 307 int numpackdirs, i; 308 309 qfiles = NULL; 310 lastqf = NULL; 311 //open the pak file 312 fp = fopen(pakfile, "rb"); 313 if (!fp) 314 { 315 Warning("can't open pak file %s", pakfile); 316 return NULL; 317 } //end if 318 //read pak header, check for valid pak id and seek to the dir entries 319 if ((fread(&packheader, 1, sizeof(dpackheader_t), fp) != sizeof(dpackheader_t)) 320 || (packheader.ident != IDPAKHEADER && packheader.ident != SINPAKHEADER) 321 || (fseek(fp, LittleLong(packheader.dirofs), SEEK_SET)) 322 ) 323 { 324 fclose(fp); 325 Warning("invalid pak file %s", pakfile); 326 return NULL; 327 } //end if 328 //if it is a pak file from id software 329 if (packheader.ident == IDPAKHEADER) 330 { 331 //number of dir entries in the pak file 332 numpackdirs = LittleLong(packheader.dirlen) / sizeof(dpackfile_t); 333 idpackfiles = (dpackfile_t *) malloc(numpackdirs * sizeof(dpackfile_t)); 334 if (!idpackfiles) Error("out of memory"); 335 //read the dir entry 336 if (fread(idpackfiles, sizeof(dpackfile_t), numpackdirs, fp) != numpackdirs) 337 { 338 fclose(fp); 339 free(idpackfiles); 340 Warning("can't read the Quake pak file dir entries from %s", pakfile); 341 return NULL; 342 } //end if 343 fclose(fp); 344 //convert to sin pack files 345 packfiles = (dsinpackfile_t *) malloc(numpackdirs * sizeof(dsinpackfile_t)); 346 if (!packfiles) Error("out of memory"); 347 for (i = 0; i < numpackdirs; i++) 348 { 349 strcpy(packfiles[i].name, idpackfiles[i].name); 350 packfiles[i].filepos = LittleLong(idpackfiles[i].filepos); 351 packfiles[i].filelen = LittleLong(idpackfiles[i].filelen); 352 } //end for 353 free(idpackfiles); 354 } //end if 355 else //its a Sin pack file 356 { 357 //number of dir entries in the pak file 358 numpackdirs = LittleLong(packheader.dirlen) / sizeof(dsinpackfile_t); 359 packfiles = (dsinpackfile_t *) malloc(numpackdirs * sizeof(dsinpackfile_t)); 360 if (!packfiles) Error("out of memory"); 361 //read the dir entry 362 if (fread(packfiles, sizeof(dsinpackfile_t), numpackdirs, fp) != numpackdirs) 363 { 364 fclose(fp); 365 free(packfiles); 366 Warning("can't read the Sin pak file dir entries from %s", pakfile); 367 return NULL; 368 } //end if 369 fclose(fp); 370 for (i = 0; i < numpackdirs; i++) 371 { 372 packfiles[i].filepos = LittleLong(packfiles[i].filepos); 373 packfiles[i].filelen = LittleLong(packfiles[i].filelen); 374 } //end for 375 } //end else 376 // 377 for (i = 0; i < numpackdirs; i++) 378 { 379 ConvertPath(packfiles[i].name); 380 if (FileFilter(filter, packfiles[i].name, false)) 381 { 382 qf = malloc(sizeof(quakefile_t)); 383 if (!qf) Error("out of memory"); 384 memset(qf, 0, sizeof(quakefile_t)); 385 strcpy(qf->pakfile, pakfile); 386 strcpy(qf->filename, pakfile); 387 strcpy(qf->origname, packfiles[i].name); 388 qf->zipfile = false; 389 qf->offset = packfiles[i].filepos; 390 qf->length = packfiles[i].filelen; 391 qf->type = QuakeFileType(packfiles[i].name); 392 //add the file ot the list 393 qf->next = NULL; 394 if (lastqf) lastqf->next = qf; 395 else qfiles = qf; 396 lastqf = qf; 397 } //end if 398 } //end for 399 free(packfiles); 400 return qfiles; 401 } //end of the function FindQuakeFilesInPak 402 //=========================================================================== 403 // 404 // Parameter: - 405 // Returns: - 406 // Changes Globals: - 407 //=========================================================================== 408 quakefile_t *FindQuakeFilesWithPakFilter(char *pakfilter, char *filter) 409 { 410 #if defined(WIN32)|defined(_WIN32) 411 WIN32_FIND_DATA filedata; 412 HWND handle; 413 struct _stat statbuf; 414 #else 415 glob_t globbuf; 416 struct stat statbuf; 417 int j; 418 #endif 419 quakefile_t *qfiles, *lastqf, *qf; 420 char pakfile[_MAX_PATH], filename[_MAX_PATH], *str; 421 int done; 422 423 qfiles = NULL; 424 lastqf = NULL; 425 if (pakfilter && strlen(pakfilter)) 426 { 427 #if defined(WIN32)|defined(_WIN32) 428 handle = FindFirstFile(pakfilter, &filedata); 429 done = (handle == INVALID_HANDLE_VALUE); 430 while(!done) 431 { 432 _splitpath(pakfilter, pakfile, NULL, NULL, NULL); 433 _splitpath(pakfilter, NULL, &pakfile[strlen(pakfile)], NULL, NULL); 434 AppendPathSeperator(pakfile, _MAX_PATH); 435 strcat(pakfile, filedata.cFileName); 436 _stat(pakfile, &statbuf); 437 #else 438 glob(pakfilter, 0, NULL, &globbuf); 439 for (j = 0; j < globbuf.gl_pathc; j++) 440 { 441 strcpy(pakfile, globbuf.gl_pathv[j]); 442 stat(pakfile, &statbuf); 443 #endif 444 //if the file with .pak or .pk3 is a folder 445 if (statbuf.st_mode & S_IFDIR) 446 { 447 strcpy(filename, pakfilter); 448 AppendPathSeperator(filename, _MAX_PATH); 449 strcat(filename, filter); 450 qf = FindQuakeFilesWithPakFilter(NULL, filename); 451 if (lastqf) lastqf->next = qf; 452 else qfiles = qf; 453 lastqf = qf; 454 while(lastqf->next) lastqf = lastqf->next; 455 } //end if 456 else 457 { 458 #if defined(WIN32)|defined(_WIN32) 459 str = StringContains(pakfile, ".pk3", false); 460 #else 461 str = StringContains(pakfile, ".pk3", true); 462 #endif 463 if (str && str == pakfile + strlen(pakfile) - strlen(".pk3")) 464 { 465 qf = FindQuakeFilesInZip(pakfile, filter); 466 } //end if 467 else 468 { 469 qf = FindQuakeFilesInPak(pakfile, filter); 470 } //end else 471 // 472 if (qf) 473 { 474 if (lastqf) lastqf->next = qf; 475 else qfiles = qf; 476 lastqf = qf; 477 while(lastqf->next) lastqf = lastqf->next; 478 } //end if 479 } //end else 480 // 481 #if defined(WIN32)|defined(_WIN32) 482 //find the next file 483 done = !FindNextFile(handle, &filedata); 484 } //end while 485 #else 486 } //end for 487 globfree(&globbuf); 488 #endif 489 } //end if 490 else 491 { 492 #if defined(WIN32)|defined(_WIN32) 493 handle = FindFirstFile(filter, &filedata); 494 done = (handle == INVALID_HANDLE_VALUE); 495 while(!done) 496 { 497 _splitpath(filter, filename, NULL, NULL, NULL); 498 _splitpath(filter, NULL, &filename[strlen(filename)], NULL, NULL); 499 AppendPathSeperator(filename, _MAX_PATH); 500 strcat(filename, filedata.cFileName); 501 #else 502 glob(filter, 0, NULL, &globbuf); 503 for (j = 0; j < globbuf.gl_pathc; j++) 504 { 505 strcpy(filename, globbuf.gl_pathv[j]); 506 #endif 507 // 508 qf = malloc(sizeof(quakefile_t)); 509 if (!qf) Error("out of memory"); 510 memset(qf, 0, sizeof(quakefile_t)); 511 strcpy(qf->pakfile, ""); 512 strcpy(qf->filename, filename); 513 strcpy(qf->origname, filename); 514 qf->offset = 0; 515 qf->length = 0; 516 qf->type = QuakeFileType(filename); 517 //add the file ot the list 518 qf->next = NULL; 519 if (lastqf) lastqf->next = qf; 520 else qfiles = qf; 521 lastqf = qf; 522 #if defined(WIN32)|defined(_WIN32) 523 //find the next file 524 done = !FindNextFile(handle, &filedata); 525 } //end while 526 #else 527 } //end for 528 globfree(&globbuf); 529 #endif 530 } //end else 531 return qfiles; 532 } //end of the function FindQuakeFilesWithPakFilter 533 //=========================================================================== 534 // 535 // Parameter: - 536 // Returns: - 537 // Changes Globals: - 538 //=========================================================================== 539 quakefile_t *FindQuakeFiles(char *filter) 540 { 541 char *str; 542 char newfilter[_MAX_PATH]; 543 char pakfilter[_MAX_PATH]; 544 char filefilter[_MAX_PATH]; 545 546 strcpy(newfilter, filter); 547 ConvertPath(newfilter); 548 strcpy(pakfilter, newfilter); 549 550 str = StringContains(pakfilter, ".pak", false); 551 if (!str) str = StringContains(pakfilter, ".pk3", false); 552 553 if (str) 554 { 555 str += strlen(".pak"); 556 if (*str) 557 { 558 *str++ = '\0'; 559 while(*str == '\\' || *str == '/') str++; 560 strcpy(filefilter, str); 561 return FindQuakeFilesWithPakFilter(pakfilter, filefilter); 562 } //end if 563 } //end else 564 return FindQuakeFilesWithPakFilter(NULL, newfilter); 565 } //end of the function FindQuakeFiles 566 //=========================================================================== 567 // 568 // Parameter: - 569 // Returns: - 570 // Changes Globals: - 571 //=========================================================================== 572 int LoadQuakeFile(quakefile_t *qf, void **bufferptr) 573 { 574 FILE *fp; 575 void *buffer; 576 int length; 577 unzFile zf; 578 579 if (qf->zipfile) 580 { 581 //open the zip file 582 zf = unzOpen(qf->pakfile); 583 //set the file pointer 584 qf->zipinfo.file = ((unz_s *) zf)->file; 585 //open the Quake file in the zip file 586 unzOpenCurrentFile(&qf->zipinfo); 587 //allocate memory for the buffer 588 length = qf->length; 589 buffer = GetMemory(length+1); 590 //read the Quake file from the zip file 591 length = unzReadCurrentFile(&qf->zipinfo, buffer, length); 592 //close the Quake file in the zip file 593 unzCloseCurrentFile(&qf->zipinfo); 594 //close the zip file 595 unzClose(zf); 596 597 *bufferptr = buffer; 598 return length; 599 } //end if 600 else 601 { 602 fp = SafeOpenRead(qf->filename); 603 if (qf->offset) fseek(fp, qf->offset, SEEK_SET); 604 length = qf->length; 605 if (!length) length = Q_filelength(fp); 606 buffer = GetMemory(length+1); 607 ((char *)buffer)[length] = 0; 608 SafeRead(fp, buffer, length); 609 fclose(fp); 610 611 *bufferptr = buffer; 612 return length; 613 } //end else 614 } //end of the function LoadQuakeFile 615 //=========================================================================== 616 // 617 // Parameter: - 618 // Returns: - 619 // Changes Globals: - 620 //=========================================================================== 621 int ReadQuakeFile(quakefile_t *qf, void *buffer, int offset, int length) 622 { 623 FILE *fp; 624 int read; 625 unzFile zf; 626 char tmpbuf[1024]; 627 628 if (qf->zipfile) 629 { 630 //open the zip file 631 zf = unzOpen(qf->pakfile); 632 //set the file pointer 633 qf->zipinfo.file = ((unz_s *) zf)->file; 634 //open the Quake file in the zip file 635 unzOpenCurrentFile(&qf->zipinfo); 636 // 637 while(offset > 0) 638 { 639 read = offset; 640 if (read > sizeof(tmpbuf)) read = sizeof(tmpbuf); 641 unzReadCurrentFile(&qf->zipinfo, tmpbuf, read); 642 offset -= read; 643 } //end while 644 //read the Quake file from the zip file 645 length = unzReadCurrentFile(&qf->zipinfo, buffer, length); 646 //close the Quake file in the zip file 647 unzCloseCurrentFile(&qf->zipinfo); 648 //close the zip file 649 unzClose(zf); 650 651 return length; 652 } //end if 653 else 654 { 655 fp = SafeOpenRead(qf->filename); 656 if (qf->offset) fseek(fp, qf->offset, SEEK_SET); 657 if (offset) fseek(fp, offset, SEEK_CUR); 658 SafeRead(fp, buffer, length); 659 fclose(fp); 660 661 return length; 662 } //end else 663 } //end of the function ReadQuakeFile