/*
** 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 <zlib.h> // 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);
}