/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #if defined(WIN32)|defined(_WIN32) #include #include #include #else #include #include #include #endif #include "qbsp.h" //file extensions with their type typedef struct qfile_exttype_s { char *extension; int type; } qfile_exttyp_t; qfile_exttyp_t quakefiletypes[] = { {QFILEEXT_UNKNOWN, QFILETYPE_UNKNOWN}, {QFILEEXT_PAK, QFILETYPE_PAK}, {QFILEEXT_PK3, QFILETYPE_PK3}, {QFILEEXT_SIN, QFILETYPE_PAK}, {QFILEEXT_BSP, QFILETYPE_BSP}, {QFILEEXT_MAP, QFILETYPE_MAP}, {QFILEEXT_MDL, QFILETYPE_MDL}, {QFILEEXT_MD2, QFILETYPE_MD2}, {QFILEEXT_MD3, QFILETYPE_MD3}, {QFILEEXT_WAL, QFILETYPE_WAL}, {QFILEEXT_WAV, QFILETYPE_WAV}, {QFILEEXT_AAS, QFILETYPE_AAS}, {NULL, 0} }; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int QuakeFileExtensionType(char *extension) { int i; for (i = 0; quakefiletypes[i].extension; i++) { if (!stricmp(extension, quakefiletypes[i].extension)) { return quakefiletypes[i].type; } //end if } //end for return QFILETYPE_UNKNOWN; } //end of the function QuakeFileExtensionType //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *QuakeFileTypeExtension(int type) { int i; for (i = 0; quakefiletypes[i].extension; i++) { if (quakefiletypes[i].type == type) { return quakefiletypes[i].extension; } //end if } //end for return QFILEEXT_UNKNOWN; } //end of the function QuakeFileExtension //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int QuakeFileType(char *filename) { char ext[_MAX_PATH] = "."; ExtractFileExtension(filename, ext+1); return QuakeFileExtensionType(ext); } //end of the function QuakeFileTypeFromFileName //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *StringContains(char *str1, char *str2, int casesensitive) { int len, i, j; len = strlen(str1) - strlen(str2); for (i = 0; i <= len; i++, str1++) { for (j = 0; str2[j]; j++) { if (casesensitive) { if (str1[j] != str2[j]) break; } //end if else { if (toupper(str1[j]) != toupper(str2[j])) break; } //end else } //end for if (!str2[j]) return str1; } //end for return NULL; } //end of the function StringContains //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int FileFilter(char *filter, char *filename, int casesensitive) { char buf[1024]; char *ptr; int i, found; while(*filter) { if (*filter == '*') { filter++; for (i = 0; *filter; i++) { if (*filter == '*' || *filter == '?') break; buf[i] = *filter; filter++; } //end for buf[i] = '\0'; if (strlen(buf)) { ptr = StringContains(filename, buf, casesensitive); if (!ptr) return false; filename = ptr + strlen(buf); } //end if } //end if else if (*filter == '?') { filter++; filename++; } //end else if else if (*filter == '[' && *(filter+1) == '[') { filter++; } //end if else if (*filter == '[') { filter++; found = false; while(*filter && !found) { if (*filter == ']' && *(filter+1) != ']') break; if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) { if (casesensitive) { if (*filename >= *filter && *filename <= *(filter+2)) found = true; } //end if else { if (toupper(*filename) >= toupper(*filter) && toupper(*filename) <= toupper(*(filter+2))) found = true; } //end else filter += 3; } //end if else { if (casesensitive) { if (*filter == *filename) found = true; } //end if else { if (toupper(*filter) == toupper(*filename)) found = true; } //end else filter++; } //end else } //end while if (!found) return false; while(*filter) { if (*filter == ']' && *(filter+1) != ']') break; filter++; } //end while filter++; filename++; } //end else if else { if (casesensitive) { if (*filter != *filename) return false; } //end if else { if (toupper(*filter) != toupper(*filename)) return false; } //end else filter++; filename++; } //end else } //end while return true; } //end of the function FileFilter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== quakefile_t *FindQuakeFilesInZip(char *zipfile, char *filter) { unzFile uf; int err; unz_global_info gi; char filename_inzip[MAX_PATH]; unz_file_info file_info; int i; quakefile_t *qfiles, *lastqf, *qf; uf = unzOpen(zipfile); err = unzGetGlobalInfo(uf, &gi); if (err != UNZ_OK) return NULL; unzGoToFirstFile(uf); qfiles = NULL; lastqf = NULL; for (i = 0; i < gi.number_entry; i++) { err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL,0,NULL,0); if (err != UNZ_OK) break; ConvertPath(filename_inzip); if (FileFilter(filter, filename_inzip, false)) { qf = malloc(sizeof(quakefile_t)); if (!qf) Error("out of memory"); memset(qf, 0, sizeof(quakefile_t)); strcpy(qf->pakfile, zipfile); strcpy(qf->filename, zipfile); strcpy(qf->origname, filename_inzip); qf->zipfile = true; //memcpy( &buildBuffer[i].zipfileinfo, (unz_s*)uf, sizeof(unz_s)); memcpy(&qf->zipinfo, (unz_s*)uf, sizeof(unz_s)); qf->offset = 0; qf->length = file_info.uncompressed_size; qf->type = QuakeFileType(filename_inzip); //add the file ot the list qf->next = NULL; if (lastqf) lastqf->next = qf; else qfiles = qf; lastqf = qf; } //end if unzGoToNextFile(uf); } //end for unzClose(uf); return qfiles; } //end of the function FindQuakeFilesInZip //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== quakefile_t *FindQuakeFilesInPak(char *pakfile, char *filter) { FILE *fp; dpackheader_t packheader; dsinpackfile_t *packfiles; dpackfile_t *idpackfiles; quakefile_t *qfiles, *lastqf, *qf; int numpackdirs, i; qfiles = NULL; lastqf = NULL; //open the pak file fp = fopen(pakfile, "rb"); if (!fp) { Warning("can't open pak file %s", pakfile); return NULL; } //end if //read pak header, check for valid pak id and seek to the dir entries if ((fread(&packheader, 1, sizeof(dpackheader_t), fp) != sizeof(dpackheader_t)) || (packheader.ident != IDPAKHEADER && packheader.ident != SINPAKHEADER) || (fseek(fp, LittleLong(packheader.dirofs), SEEK_SET)) ) { fclose(fp); Warning("invalid pak file %s", pakfile); return NULL; } //end if //if it is a pak file from id software if (packheader.ident == IDPAKHEADER) { //number of dir entries in the pak file numpackdirs = LittleLong(packheader.dirlen) / sizeof(dpackfile_t); idpackfiles = (dpackfile_t *) malloc(numpackdirs * sizeof(dpackfile_t)); if (!idpackfiles) Error("out of memory"); //read the dir entry if (fread(idpackfiles, sizeof(dpackfile_t), numpackdirs, fp) != numpackdirs) { fclose(fp); free(idpackfiles); Warning("can't read the Quake pak file dir entries from %s", pakfile); return NULL; } //end if fclose(fp); //convert to sin pack files packfiles = (dsinpackfile_t *) malloc(numpackdirs * sizeof(dsinpackfile_t)); if (!packfiles) Error("out of memory"); for (i = 0; i < numpackdirs; i++) { strcpy(packfiles[i].name, idpackfiles[i].name); packfiles[i].filepos = LittleLong(idpackfiles[i].filepos); packfiles[i].filelen = LittleLong(idpackfiles[i].filelen); } //end for free(idpackfiles); } //end if else //its a Sin pack file { //number of dir entries in the pak file numpackdirs = LittleLong(packheader.dirlen) / sizeof(dsinpackfile_t); packfiles = (dsinpackfile_t *) malloc(numpackdirs * sizeof(dsinpackfile_t)); if (!packfiles) Error("out of memory"); //read the dir entry if (fread(packfiles, sizeof(dsinpackfile_t), numpackdirs, fp) != numpackdirs) { fclose(fp); free(packfiles); Warning("can't read the Sin pak file dir entries from %s", pakfile); return NULL; } //end if fclose(fp); for (i = 0; i < numpackdirs; i++) { packfiles[i].filepos = LittleLong(packfiles[i].filepos); packfiles[i].filelen = LittleLong(packfiles[i].filelen); } //end for } //end else // for (i = 0; i < numpackdirs; i++) { ConvertPath(packfiles[i].name); if (FileFilter(filter, packfiles[i].name, false)) { qf = malloc(sizeof(quakefile_t)); if (!qf) Error("out of memory"); memset(qf, 0, sizeof(quakefile_t)); strcpy(qf->pakfile, pakfile); strcpy(qf->filename, pakfile); strcpy(qf->origname, packfiles[i].name); qf->zipfile = false; qf->offset = packfiles[i].filepos; qf->length = packfiles[i].filelen; qf->type = QuakeFileType(packfiles[i].name); //add the file ot the list qf->next = NULL; if (lastqf) lastqf->next = qf; else qfiles = qf; lastqf = qf; } //end if } //end for free(packfiles); return qfiles; } //end of the function FindQuakeFilesInPak //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== quakefile_t *FindQuakeFilesWithPakFilter(char *pakfilter, char *filter) { #if defined(WIN32)|defined(_WIN32) WIN32_FIND_DATA filedata; HWND handle; struct _stat statbuf; #else glob_t globbuf; struct stat statbuf; int j; #endif quakefile_t *qfiles, *lastqf, *qf; char pakfile[_MAX_PATH], filename[_MAX_PATH], *str; int done; qfiles = NULL; lastqf = NULL; if (pakfilter && strlen(pakfilter)) { #if defined(WIN32)|defined(_WIN32) handle = FindFirstFile(pakfilter, &filedata); done = (handle == INVALID_HANDLE_VALUE); while(!done) { _splitpath(pakfilter, pakfile, NULL, NULL, NULL); _splitpath(pakfilter, NULL, &pakfile[strlen(pakfile)], NULL, NULL); AppendPathSeperator(pakfile, _MAX_PATH); strcat(pakfile, filedata.cFileName); _stat(pakfile, &statbuf); #else glob(pakfilter, 0, NULL, &globbuf); for (j = 0; j < globbuf.gl_pathc; j++) { strcpy(pakfile, globbuf.gl_pathv[j]); stat(pakfile, &statbuf); #endif //if the file with .pak or .pk3 is a folder if (statbuf.st_mode & S_IFDIR) { strcpy(filename, pakfilter); AppendPathSeperator(filename, _MAX_PATH); strcat(filename, filter); qf = FindQuakeFilesWithPakFilter(NULL, filename); if (lastqf) lastqf->next = qf; else qfiles = qf; lastqf = qf; while(lastqf->next) lastqf = lastqf->next; } //end if else { #if defined(WIN32)|defined(_WIN32) str = StringContains(pakfile, ".pk3", false); #else str = StringContains(pakfile, ".pk3", true); #endif if (str && str == pakfile + strlen(pakfile) - strlen(".pk3")) { qf = FindQuakeFilesInZip(pakfile, filter); } //end if else { qf = FindQuakeFilesInPak(pakfile, filter); } //end else // if (qf) { if (lastqf) lastqf->next = qf; else qfiles = qf; lastqf = qf; while(lastqf->next) lastqf = lastqf->next; } //end if } //end else // #if defined(WIN32)|defined(_WIN32) //find the next file done = !FindNextFile(handle, &filedata); } //end while #else } //end for globfree(&globbuf); #endif } //end if else { #if defined(WIN32)|defined(_WIN32) handle = FindFirstFile(filter, &filedata); done = (handle == INVALID_HANDLE_VALUE); while(!done) { _splitpath(filter, filename, NULL, NULL, NULL); _splitpath(filter, NULL, &filename[strlen(filename)], NULL, NULL); AppendPathSeperator(filename, _MAX_PATH); strcat(filename, filedata.cFileName); #else glob(filter, 0, NULL, &globbuf); for (j = 0; j < globbuf.gl_pathc; j++) { strcpy(filename, globbuf.gl_pathv[j]); #endif // qf = malloc(sizeof(quakefile_t)); if (!qf) Error("out of memory"); memset(qf, 0, sizeof(quakefile_t)); strcpy(qf->pakfile, ""); strcpy(qf->filename, filename); strcpy(qf->origname, filename); qf->offset = 0; qf->length = 0; qf->type = QuakeFileType(filename); //add the file ot the list qf->next = NULL; if (lastqf) lastqf->next = qf; else qfiles = qf; lastqf = qf; #if defined(WIN32)|defined(_WIN32) //find the next file done = !FindNextFile(handle, &filedata); } //end while #else } //end for globfree(&globbuf); #endif } //end else return qfiles; } //end of the function FindQuakeFilesWithPakFilter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== quakefile_t *FindQuakeFiles(char *filter) { char *str; char newfilter[_MAX_PATH]; char pakfilter[_MAX_PATH]; char filefilter[_MAX_PATH]; strcpy(newfilter, filter); ConvertPath(newfilter); strcpy(pakfilter, newfilter); str = StringContains(pakfilter, ".pak", false); if (!str) str = StringContains(pakfilter, ".pk3", false); if (str) { str += strlen(".pak"); if (*str) { *str++ = '\0'; while(*str == '\\' || *str == '/') str++; strcpy(filefilter, str); return FindQuakeFilesWithPakFilter(pakfilter, filefilter); } //end if } //end else return FindQuakeFilesWithPakFilter(NULL, newfilter); } //end of the function FindQuakeFiles //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int LoadQuakeFile(quakefile_t *qf, void **bufferptr) { FILE *fp; void *buffer; int length; unzFile zf; if (qf->zipfile) { //open the zip file zf = unzOpen(qf->pakfile); //set the file pointer qf->zipinfo.file = ((unz_s *) zf)->file; //open the Quake file in the zip file unzOpenCurrentFile(&qf->zipinfo); //allocate memory for the buffer length = qf->length; buffer = GetMemory(length+1); //read the Quake file from the zip file length = unzReadCurrentFile(&qf->zipinfo, buffer, length); //close the Quake file in the zip file unzCloseCurrentFile(&qf->zipinfo); //close the zip file unzClose(zf); *bufferptr = buffer; return length; } //end if else { fp = SafeOpenRead(qf->filename); if (qf->offset) fseek(fp, qf->offset, SEEK_SET); length = qf->length; if (!length) length = Q_filelength(fp); buffer = GetMemory(length+1); ((char *)buffer)[length] = 0; SafeRead(fp, buffer, length); fclose(fp); *bufferptr = buffer; return length; } //end else } //end of the function LoadQuakeFile //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int ReadQuakeFile(quakefile_t *qf, void *buffer, int offset, int length) { FILE *fp; int read; unzFile zf; char tmpbuf[1024]; if (qf->zipfile) { //open the zip file zf = unzOpen(qf->pakfile); //set the file pointer qf->zipinfo.file = ((unz_s *) zf)->file; //open the Quake file in the zip file unzOpenCurrentFile(&qf->zipinfo); // while(offset > 0) { read = offset; if (read > sizeof(tmpbuf)) read = sizeof(tmpbuf); unzReadCurrentFile(&qf->zipinfo, tmpbuf, read); offset -= read; } //end while //read the Quake file from the zip file length = unzReadCurrentFile(&qf->zipinfo, buffer, length); //close the Quake file in the zip file unzCloseCurrentFile(&qf->zipinfo); //close the zip file unzClose(zf); return length; } //end if else { fp = SafeOpenRead(qf->filename); if (qf->offset) fseek(fp, qf->offset, SEEK_SET); if (offset) fseek(fp, offset, SEEK_CUR); SafeRead(fp, buffer, length); fclose(fp); return length; } //end else } //end of the function ReadQuakeFile