l_qfiles.c 17 KB

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