/* ** Haaf's Game Engine 1.8 ** Copyright (C) 2003-2007, Relish Games ** hge.relishgames.com ** ** Core functions implementation: resources management */ #include "hge_impl.h" #include // the system version is better here. HGE's is out of date. #define NOCRYPT //#define NOUNCRYPT #include "ZLIB/unzip.h" bool CALL HGE_Impl::Resource_AttachPack(const char *filename, const char *password) { char *szName; CResourceList *resItem=res; unzFile zip; szName=Resource_MakePath(filename); while(resItem) { if(!strcmp(szName,resItem->filename)) return false; resItem=resItem->next; } zip=unzOpen(szName); if(!zip) { System_Log("Unable to unzip: %s", szName); return false; } unzClose(zip); resItem=new CResourceList; strcpy(resItem->filename, szName); if(password) strcpy(resItem->password, password); else resItem->password[0]=0; resItem->next=res; res=resItem; return true; } void CALL HGE_Impl::Resource_RemovePack(const char *filename) { char *szName; CResourceList *resItem=res, *resPrev=0; szName=Resource_MakePath(filename); while(resItem) { if(!strcmp(szName,resItem->filename)) { if(resPrev) resPrev->next=resItem->next; else res=resItem->next; delete resItem; break; } resPrev=resItem; resItem=resItem->next; } } void CALL HGE_Impl::Resource_RemoveAllPacks() { CResourceList *resItem=res, *resNextItem; while(resItem) { resNextItem=resItem->next; delete resItem; resItem=resNextItem; } res=0; } void* CALL HGE_Impl::Resource_Load(const char *filename, DWORD *size) { const char *res_err="Can't load resource: %s"; CResourceList *resItem=res; char szName[_MAX_PATH]; char szZipName[_MAX_PATH]; unzFile zip; unz_file_info file_info; int done, i; void *ptr; FILE *hF; if(filename[0]=='\\' || filename[0]=='/' || filename[1]==':') goto _fromfile; // skip absolute paths // Load from pack strcpy(szName,filename); for(i=0; szName[i]; i++) { if(szName[i]=='/') szName[i]='\\'; } while(resItem) { zip=unzOpen(resItem->filename); done=unzGoToFirstFile(zip); while(done==UNZ_OK) { unzGetCurrentFileInfo(zip, &file_info, szZipName, sizeof(szZipName), NULL, 0, NULL, 0); for(i=0; szZipName[i]; i++) { if(szZipName[i]=='/') szZipName[i]='\\'; } if(!strcmp(szName,szZipName)) { if(unzOpenCurrentFilePassword(zip, resItem->password[0] ? resItem->password : 0) != UNZ_OK) { unzClose(zip); sprintf(szName, res_err, filename); _PostError(szName); return 0; } ptr = malloc(file_info.uncompressed_size); if(!ptr) { unzCloseCurrentFile(zip); unzClose(zip); sprintf(szName, res_err, filename); _PostError(szName); return 0; } if(unzReadCurrentFile(zip, ptr, file_info.uncompressed_size) < 0) { unzCloseCurrentFile(zip); unzClose(zip); free(ptr); sprintf(szName, res_err, filename); _PostError(szName); return 0; } unzCloseCurrentFile(zip); unzClose(zip); if(size) *size=file_info.uncompressed_size; return ptr; } done=unzGoToNextFile(zip); } unzClose(zip); resItem=resItem->next; } // Load from file _fromfile: hF = fopen(Resource_MakePath(filename), "rb"); if(hF == NULL) { sprintf(szName, res_err, filename); _PostError(szName); return 0; } struct stat statbuf; if (fstat(fileno(hF), &statbuf) == -1) { fclose(hF); sprintf(szName, res_err, filename); _PostError(szName); return 0; } file_info.uncompressed_size = statbuf.st_size; ptr = malloc(file_info.uncompressed_size); if(!ptr) { fclose(hF); sprintf(szName, res_err, filename); _PostError(szName); return 0; } if(fread(ptr, file_info.uncompressed_size, 1, hF) != 1) { fclose(hF); free(ptr); sprintf(szName, res_err, filename); _PostError(szName); return 0; } fclose(hF); if(size) *size=file_info.uncompressed_size; return ptr; } void CALL HGE_Impl::Resource_Free(void *res) { if(res) free(res); } // this is from PhysicsFS originally ( http://icculus.org/physfs/ ) // (also zlib-licensed.) static int locateOneElement(char *buf) { char *ptr = NULL; DIR *dirp = NULL; struct dirent *dent = NULL; if (access(buf, F_OK) == 0) return 1; /* quick rejection: exists in current case. */ ptr = strrchr(buf, '/'); /* find entry at end of path. */ if (ptr == NULL) { dirp = opendir("."); ptr = buf; } else { *ptr = '\0'; dirp = opendir(buf); *ptr = '/'; ptr++; /* point past dirsep to entry itself. */ } while ((dent = readdir(dirp)) != NULL) { if (strcasecmp(dent->d_name, ptr) == 0) { strcpy(ptr, dent->d_name); /* found a match. Overwrite with this case. */ closedir(dirp); return 1; } } /* no match at all... */ closedir(dirp); return 0; } static int locateCorrectCase(char *buf) { char *ptr = buf; while ((ptr = strchr(ptr + 1, '/'))) { *ptr = '\0'; /* block this path section off */ if (!locateOneElement(buf)) { *ptr = '/'; /* restore path separator */ return -2; /* missing element in path. */ } *ptr = '/'; /* restore path separator */ } /* check final element... */ return locateOneElement(buf) ? 0 : -1; } char* CALL HGE_Impl::Resource_MakePath(const char *filename) { int i; if(!filename) strcpy(szTmpFilename, szAppPath); else if(filename[0]=='\\' || filename[0]=='/' || filename[1]==':') strcpy(szTmpFilename, filename); else { strcpy(szTmpFilename, szAppPath); if(filename) strcat(szTmpFilename, filename); } for(i=0; szTmpFilename[i]; i++) { if(szTmpFilename[i]=='\\') szTmpFilename[i]='/'; } locateCorrectCase(szTmpFilename); return szTmpFilename; } // !!! FIXME: kinda messy, and probably doesn't get all the corner cases right. bool HGE_Impl::_WildcardMatch(const char *str, const char *wildcard) { if ((str == NULL) || (wildcard == NULL)) return false; while ((*str) && (*wildcard)) { const char wildch = *wildcard; const char strch = *str; if (wildch == '?') ; // okay. else if (wildch == '*') { do { wildcard++; } while (((*wildcard == '*') || (*wildcard == '?')) && (*wildcard != '\0')); const char newwild = *wildcard; if (newwild == '\0') return true; const char *ptr = str; while (*ptr) // find the greediest match possible... { if (*ptr == newwild) str = ptr; ptr++; } } else if ( (toupper(strch)) != (toupper(wildch)) ) { return false; } str++; wildcard++; } while (*wildcard == '*') wildcard++; return ((*str == '\0') && (*wildcard == '\0')); } bool HGE_Impl::_PrepareFileEnum(const char *wildcard) { if(hSearch) { closedir(hSearch); hSearch=0; } char *madepath = Resource_MakePath(wildcard); const char *fname = strrchr(madepath, '/'); const char *dir = NULL; if (fname == NULL) { dir = "."; fname = madepath; } else { dir = madepath; char *ptr = (char *) fname; *ptr = '\0'; // split dir and filename. fname++; } strcpy(szSearchDir, dir); strcpy(szSearchWildcard, fname); hSearch=opendir(dir); return (hSearch!=0); } char *HGE_Impl::_DoEnumIteration(const bool wantdir) { if(!hSearch) return 0; while (true) { struct dirent *dent = readdir(hSearch); if(dent == NULL) { closedir(hSearch); hSearch=0; return 0; } if ((strcmp(dent->d_name, ".") == 0) || (strcmp(dent->d_name, "..") == 0)) continue; if (!_WildcardMatch(dent->d_name, szSearchWildcard)) continue; char fullpath[_MAX_PATH]; snprintf(fullpath, sizeof (fullpath), "%s/%s", szSearchDir, dent->d_name); struct stat statbuf; if (stat(fullpath, &statbuf) == -1) // this follows symlinks. continue; const bool isdir = ((S_ISDIR(statbuf.st_mode)) != 0); if (isdir == wantdir) // this treats pipes, devs, etc, as "files" ... { strcpy(szSearchResult, dent->d_name); return szSearchResult; } } return 0; } char* CALL HGE_Impl::Resource_EnumFiles(const char *wildcard) { if(wildcard) { if (!_PrepareFileEnum(wildcard)) return 0; } return _DoEnumIteration(false); } char* CALL HGE_Impl::Resource_EnumFolders(const char *wildcard) { if(wildcard) { if (!_PrepareFileEnum(wildcard)) return 0; } return _DoEnumIteration(true); }