1
0
Fork 0
forked from fte/fteqw
fteqw/engine/common/fs_zip.c
Spoike 8d5b217266 ------------------------------------------------------------------------
r4169 | acceptthis | 2013-01-17 08:55:12 +0000 (Thu, 17 Jan 2013) | 31 lines

removed MAX_VISEDICTS limit.
PEXT2_REPLACEMENTDELTAS tweaked, now has 4 million entity limit. still not enabled by default.
TE_BEAM now maps to a separate TEQW_BEAM to avoid conflicts with QW.
added android multitouch emulation for windows/rawinput (in_simulatemultitouch).
split topcolor/bottomcolor from scoreboard, for dp's colormap|1024 feature.
now using utf-8 for windows consoles.
qcc warnings/errors now give clickable console links for quick+easy editing.
disabled menutint when the currently active item changes contrast or gamma (for OneManClan).
Added support for drawfont/drawfontscale.
tweaked the qcvm a little to reduce the number of pointers.
.doll file loading. still experimental and will likely crash. requires csqc active, even if its a dummy progs. this will be fixed in time. Still other things that need cleaning up.
windows: gl_font "?" shows the standard windows font-selection dialog, and can be used to select windows fonts. not all work. and you probably don't want to use windings.
fixed splitscreen support when playing mvds. added mini-scoreboards to splitscreen. 
editor/debugger now shows asm if there's no linenumber info. also, pressing f1 for help shows the shortcuts.
Added support for .framegroups files for psk(psa) and iqm formats.
True support for ezquake's colour codes. Mutually exclusive with background colours.
path command output slightly more readable.
added support for digest_hex (MD4, SHA1, CRC16).
skingroups now colourmap correctly.
Fix terrain colour hints, and litdata from the wrong bsp.
fix ftp dual-homed issue. support epsv command, and enable ipv6 (eprt still not supported).
remove d3d11 compilation from the makefile. the required headers are not provided by mingw, and are not available to the build bot, so don't bother.
fix v *= v.x and similar opcodes.
fteqcc: fixed support for áéíóú type chars in names. utf-8 files now properly supported (even with the utf-8 bom/identifier). utf-16 also supported.
fteqcc: fixed '#if 1 == 3 && 4' parsing.
fteqcc: -Werror acts on the warning, rather than as a separate error. Line numbers are thus more readable.
fteqcc: copyright message now includes compile date instead.
fteqccgui: the treeview control is now coloured depending on whether there were warnings/errors in the last compile.
fteqccgui: the output window is now focused and scrolls down as compilation progresses.
pr_dumpplatform command dumps out some pragmas to convert more serious warnings to errors. This is to avoid the infamous 'fteqcc sucks cos my code sucks' issue.
rewrote prespawn/modelist/soundlist code. server tracks progress now.
------------------------------------------------------------------------


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4167 fc73d0e0-1445-4013-8a0c-d673dee63da5
2013-03-12 22:29:40 +00:00

711 lines
16 KiB
C

#include "quakedef.h"
#include "fs.h"
#ifdef AVAIL_ZLIB
#ifndef ZEXPORT
#define ZEXPORT VARGS
#endif
#include <zlib.h>
typedef struct {
unsigned char ident1;
unsigned char ident2;
unsigned char cm;
unsigned char flags;
unsigned int mtime;
unsigned char xflags;
unsigned char os;
} gzheader_t;
#define sizeofgzheader_t 10
#define GZ_FTEXT 1
#define GZ_FHCRC 2
#define GZ_FEXTRA 4
#define GZ_FNAME 8
#define GZ_FCOMMENT 16
#define GZ_RESERVED (32|64|128)
#ifdef DYNAMIC_ZLIB
#define ZLIB_LOADED() (zlib_handle!=NULL)
void *zlib_handle;
#define ZSTATIC(n)
#else
#define ZLIB_LOADED() 1
#define ZSTATIC(n) = &n
#ifdef _MSC_VER
# ifdef _WIN64
# pragma comment(lib, MSVCLIBSPATH "zlib64.lib")
# else
# pragma comment(lib, MSVCLIBSPATH "zlib.lib")
# endif
#endif
#endif
//#pragma comment(lib, MSVCLIBSPATH "zlib.lib")
static int (ZEXPORT *qinflateEnd) (z_streamp strm) ZSTATIC(inflateEnd);
static int (ZEXPORT *qinflate) (z_streamp strm, int flush) ZSTATIC(inflate);
static int (ZEXPORT *qinflateInit2_) (z_streamp strm, int windowBits,
const char *version, int stream_size) ZSTATIC(inflateInit2_);
static uLong (ZEXPORT *qcrc32) (uLong crc, const Bytef *buf, uInt len) ZSTATIC(crc32);
#define qinflateInit2(strm, windowBits) \
qinflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
qboolean LibZ_Init(void)
{
#ifdef DYNAMIC_ZLIB
static dllfunction_t funcs[] =
{
{(void*)&qinflateEnd, "inflateEnd"},
{(void*)&qinflate, "inflate"},
{(void*)&qinflateInit2_, "inflateInit2_"},
{(void*)&qcrc32, "crc32"},
{NULL, NULL}
};
if (!ZLIB_LOADED())
zlib_handle = Sys_LoadLibrary("zlib1", funcs);
#endif
return ZLIB_LOADED();
}
vfsfile_t *FS_DecompressGZip(vfsfile_t *infile)
{
char inchar;
unsigned short inshort;
vfsfile_t *temp;
gzheader_t header;
if (VFS_READ(infile, &header, sizeofgzheader_t) == sizeofgzheader_t)
{
if (header.ident1 != 0x1f || header.ident2 != 0x8b || header.cm != 8 || header.flags & GZ_RESERVED)
{
VFS_SEEK(infile, 0);
return infile;
}
}
else
{
VFS_SEEK(infile, 0);
return infile;
}
if (header.flags & GZ_FEXTRA)
{
VFS_READ(infile, &inshort, sizeof(inshort));
inshort = LittleShort(inshort);
VFS_SEEK(infile, VFS_TELL(infile) + inshort);
}
if (header.flags & GZ_FNAME)
{
Con_Printf("gzipped file name: ");
do {
if (VFS_READ(infile, &inchar, sizeof(inchar)) != 1)
break;
Con_Printf("%c", inchar);
} while(inchar);
Con_Printf("\n");
}
if (header.flags & GZ_FCOMMENT)
{
Con_Printf("gzipped file comment: ");
do {
if (VFS_READ(infile, &inchar, sizeof(inchar)) != 1)
break;
Con_Printf("%c", inchar);
} while(inchar);
Con_Printf("\n");
}
if (header.flags & GZ_FHCRC)
{
VFS_READ(infile, &inshort, sizeof(inshort));
}
temp = FS_OpenTemp();
if (!temp)
{
VFS_SEEK(infile, 0); //doh
return infile;
}
{
unsigned char inbuffer[16384];
unsigned char outbuffer[16384];
int ret;
z_stream strm = {
inbuffer,
0,
0,
outbuffer,
sizeof(outbuffer),
0,
NULL,
NULL,
NULL,
NULL,
NULL,
Z_UNKNOWN,
0,
0
};
strm.avail_in = VFS_READ(infile, inbuffer, sizeof(inbuffer));
strm.next_in = inbuffer;
qinflateInit2(&strm, -MAX_WBITS);
while ((ret=qinflate(&strm, Z_SYNC_FLUSH)) != Z_STREAM_END)
{
if (strm.avail_in == 0 || strm.avail_out == 0)
{
if (strm.avail_in == 0)
{
strm.avail_in = VFS_READ(infile, inbuffer, sizeof(inbuffer));
strm.next_in = inbuffer;
if (!strm.avail_in)
break;
}
if (strm.avail_out == 0)
{
strm.next_out = outbuffer;
VFS_WRITE(temp, outbuffer, strm.total_out);
strm.total_out = 0;
strm.avail_out = sizeof(outbuffer);
}
continue;
}
//doh, it terminated for no reason
if (ret != Z_STREAM_END)
{
qinflateEnd(&strm);
Con_Printf("Couldn't decompress gz file\n");
VFS_CLOSE(temp);
VFS_CLOSE(infile);
return NULL;
}
}
//we got to the end
VFS_WRITE(temp, outbuffer, strm.total_out);
qinflateEnd(&strm);
VFS_SEEK(temp, 0);
}
VFS_CLOSE(infile);
return temp;
}
#include "unzip.c"
typedef struct
{
fsbucket_t bucket;
char name[MAX_QPATH];
int filepos, filelen;
} zpackfile_t;
typedef struct zipfile_s
{
char filename[MAX_OSPATH];
unzFile handle;
int numfiles;
zpackfile_t *files;
#ifdef HASH_FILESYSTEM
hashtable_t hash;
#endif
vfsfile_t *raw;
vfsfile_t *currentfile; //our unzip.c can only handle one active file at any one time
//so we have to keep closing and switching.
//slow, but it works. most of the time we'll only have a single file open anyway.
int references; //and a reference count
} zipfile_t;
static void FSZIP_GetDisplayPath(void *handle, char *out, unsigned int outlen)
{
zipfile_t *zip = handle;
if (zip->references != 1)
Q_snprintfz(out, outlen, "%s (%i)\n", zip->filename, zip->references-1);
else
Q_strncpyz(out, zip->filename, outlen);
}
static void FSZIP_ClosePath(void *handle)
{
zipfile_t *zip = handle;
if (--zip->references > 0)
return; //not yet time
unzClose(zip->handle);
if (zip->files)
Z_Free(zip->files);
Z_Free(zip);
}
static void FSZIP_BuildHash(void *handle, int depth)
{
zipfile_t *zip = handle;
int i;
for (i = 0; i < zip->numfiles; i++)
{
FS_AddFileHash(depth, zip->files[i].name, &zip->files[i].bucket, &zip->files[i]);
}
}
static qboolean FSZIP_FLocate(void *handle, flocation_t *loc, const char *filename, void *hashedresult)
{
zpackfile_t *pf = hashedresult;
int i, len;
zipfile_t *zip = handle;
// look through all the pak file elements
if (pf)
{ //is this a pointer to a file in this pak?
if (pf < zip->files || pf >= zip->files + zip->numfiles)
return false; //was found in a different path
}
else
{
for (i=0 ; i<zip->numfiles ; i++) //look for the file
{
if (!stricmp (zip->files[i].name, filename))
{
pf = &zip->files[i];
break;
}
}
}
if (pf)
{
len = pf->filelen;
if (loc)
{
loc->index = pf - zip->files;
strcpy(loc->rawname, zip->filename);
loc->offset = pf->filepos;
loc->len = pf->filelen;
unzLocateFileMy (zip->handle, loc->index, zip->files[loc->index].filepos);
loc->offset = unzGetCurrentFileUncompressedPos(zip->handle);
// if (loc->offset<0)
// { //file not found, or is compressed.
// *loc->rawname = '\0';
// loc->offset=0;
// }
}
return true;
}
return false;
}
static void FSZIP_ReadFile(void *handle, flocation_t *loc, char *buffer)
{
zipfile_t *zip = handle;
int err;
unzLocateFileMy (zip->handle, loc->index, zip->files[loc->index].filepos);
unzOpenCurrentFile (zip->handle);
err = unzReadCurrentFile (zip->handle, buffer, zip->files[loc->index].filelen);
unzCloseCurrentFile (zip->handle);
if (err!=zip->files[loc->index].filelen)
{
Con_Printf ("Can't extract file \"%s:%s\" (corrupt)\n", zip->filename, zip->files[loc->index].name);
return;
}
return;
}
static int FSZIP_EnumerateFiles (void *handle, const char *match, int (*func)(const char *, int, void *), void *parm)
{
zipfile_t *zip = handle;
int num;
for (num = 0; num<(int)zip->numfiles; num++)
{
if (wildcmp(match, zip->files[num].name))
{
if (!func(zip->files[num].name, zip->files[num].filelen, parm))
return false;
}
}
return true;
}
/*
=================
COM_LoadZipFile
Takes an explicit (not game tree related) path to a pak file.
Loads the header and directory, adding the files at the beginning
of the list so they override previous pack files.
=================
*/
static void *FSZIP_LoadZipFile (vfsfile_t *packhandle, const char *desc)
{
int i;
int nextfileziphandle;
zipfile_t *zip;
zpackfile_t *newfiles;
unz_global_info globalinf = {0};
unz_file_info file_info;
zip = Z_Malloc(sizeof(zipfile_t));
Q_strncpyz(zip->filename, desc, sizeof(zip->filename));
zip->handle = unzOpen ((zip->raw = packhandle));
if (!zip->handle)
{
Z_Free(zip);
Con_TPrintf (TL_COULDNTOPENZIP, desc);
return NULL;
}
unzGetGlobalInfo (zip->handle, &globalinf);
zip->numfiles = globalinf.number_entry;
zip->files = newfiles = Z_Malloc (zip->numfiles * sizeof(zpackfile_t));
for (i = 0; i < zip->numfiles; i++)
{
if (unzGetCurrentFileInfo (zip->handle, &file_info, newfiles[i].name, sizeof(newfiles[i].name), NULL, 0, NULL, 0) != UNZ_OK)
Con_Printf("Zip Error\n");
Q_strlwr(newfiles[i].name);
if (!*newfiles[i].name || newfiles[i].name[strlen(newfiles[i].name)-1] == '/')
newfiles[i].filelen = -1;
else
newfiles[i].filelen = file_info.uncompressed_size;
newfiles[i].filepos = file_info.c_offset;
nextfileziphandle = unzGoToNextFile (zip->handle);
if (nextfileziphandle == UNZ_END_OF_LIST_OF_FILE)
break;
else if (nextfileziphandle != UNZ_OK)
Con_Printf("Zip Error\n");
}
zip->references = 1;
zip->currentfile = NULL;
Con_TPrintf (TL_ADDEDZIPFILE, desc, zip->numfiles);
return zip;
}
int FSZIP_GeneratePureCRC(void *handle, int seed, int crctype)
{
zipfile_t *zip = handle;
unz_file_info file_info;
int result;
int *filecrcs;
int numcrcs=0;
int i;
filecrcs = BZ_Malloc((zip->numfiles+1)*sizeof(int));
filecrcs[numcrcs++] = seed;
unzGoToFirstFile(zip->handle);
for (i = 0; i < zip->numfiles; i++)
{
if (zip->files[i].filelen>0)
{
unzGetCurrentFileInfo (zip->handle, &file_info, NULL, 0, NULL, 0, NULL, 0);
filecrcs[numcrcs++] = file_info.crc;
}
unzGoToNextFile (zip->handle);
}
if (crctype)
result = Com_BlockChecksum(filecrcs, numcrcs*sizeof(int));
else
result = Com_BlockChecksum(filecrcs+1, (numcrcs-1)*sizeof(int));
BZ_Free(filecrcs);
return result;
}
typedef struct {
vfsfile_t funcs;
vfsfile_t *defer;
//in case we're forced away.
zipfile_t *parent;
qboolean iscompressed;
int pos;
int length; //try and optimise some things
int index;
int startpos;
} vfszip_t;
qboolean VFSZIP_MakeActive(vfszip_t *vfsz)
{
int i;
char buffer[8192]; //must be power of two
if ((vfszip_t*)vfsz->parent->currentfile == vfsz)
return true; //already us
if (vfsz->parent->currentfile)
unzCloseCurrentFile(vfsz->parent->handle);
unzLocateFileMy(vfsz->parent->handle, vfsz->index, vfsz->startpos);
if (unzOpenCurrentFile(vfsz->parent->handle) == UNZ_BADZIPFILE)
{
unz_file_info file_info;
buffer[0] = '?';
buffer[1] = 0;
if (unzGetCurrentFileInfo (vfsz->parent->handle, &file_info, buffer, sizeof(buffer), NULL, 0, NULL, 0) != UNZ_OK)
Con_Printf("Zip Error\n");
if (file_info.compression_method && file_info.compression_method != Z_DEFLATED)
Con_Printf("unsupported compression method on %s/%s\n", vfsz->parent->filename, buffer);
else
Con_Printf("corrupt file within zip, %s/%s\n", vfsz->parent->filename, buffer);
vfsz->parent->currentfile = NULL;
return false;
}
if (vfsz->pos > 0)
{
Con_DPrintf("VFSZIP_MakeActive: Shockingly inefficient\n");
//now we need to seek up to where we had previously gotten to.
for (i = 0; i < vfsz->pos-sizeof(buffer); i++)
unzReadCurrentFile(vfsz->parent->handle, buffer, sizeof(buffer));
unzReadCurrentFile(vfsz->parent->handle, buffer, vfsz->pos - i);
}
vfsz->parent->currentfile = (vfsfile_t*)vfsz;
return true;
}
int VFSZIP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
int read;
vfszip_t *vfsz = (vfszip_t*)file;
if (vfsz->defer)
return VFS_READ(vfsz->defer, buffer, bytestoread);
if (vfsz->iscompressed)
{
if (!VFSZIP_MakeActive(vfsz))
return 0;
read = unzReadCurrentFile(vfsz->parent->handle, buffer, bytestoread);
}
else
{
if (vfsz->parent->currentfile != file)
{
unzCloseCurrentFile(vfsz->parent->handle);
VFS_SEEK(vfsz->parent->raw, vfsz->pos+vfsz->startpos);
vfsz->parent->currentfile = file;
}
if (vfsz->pos + bytestoread > vfsz->length)
bytestoread = max(0, vfsz->length - vfsz->pos);
read = VFS_READ(vfsz->parent->raw, buffer, bytestoread);
}
vfsz->pos += read;
return read;
}
int VFSZIP_WriteBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
Sys_Error("VFSZIP_WriteBytes: Not supported\n");
return 0;
}
qboolean VFSZIP_Seek (struct vfsfile_s *file, unsigned long pos)
{
vfszip_t *vfsz = (vfszip_t*)file;
if (vfsz->defer)
return VFS_SEEK(vfsz->defer, pos);
//This is *really* inefficient
if (vfsz->iscompressed)
{ //if they're going to seek on a file in a zip, let's just copy it out
char buffer[8192];
unsigned int chunk;
unsigned int i;
unsigned int length;
vfsz->defer = FS_OpenTemp();
if (vfsz->defer)
{
if (vfsz->pos)
{
unzCloseCurrentFile(vfsz->parent->handle);
vfsz->parent->currentfile = NULL; //make it not us
}
length = vfsz->length;
i = 0;
vfsz->pos = 0;
if (!VFSZIP_MakeActive(vfsz))
{
/*shouldn't really happen*/
VFS_CLOSE(vfsz->defer);
vfsz->defer = NULL;
return false;
}
while (1)
{
chunk = length - i;
if (chunk > sizeof(buffer))
chunk = sizeof(buffer);
if (chunk == 0)
break;
unzReadCurrentFile(vfsz->parent->handle, buffer, chunk);
VFS_WRITE(vfsz->defer, buffer, chunk);
i += chunk;
}
}
unzCloseCurrentFile(vfsz->parent->handle);
vfsz->parent->currentfile = NULL; //make it not us
if (vfsz->defer)
return VFS_SEEK(vfsz->defer, pos);
else
{
unzCloseCurrentFile(vfsz->parent->handle);
vfsz->parent->currentfile = NULL; //make it not us, so the next read starts at the right place
}
}
else
{
vfsz->parent->currentfile = NULL;
}
if (pos < 0 || pos > vfsz->length)
return false;
vfsz->pos = pos;
return true;
}
unsigned long VFSZIP_Tell (struct vfsfile_s *file)
{
vfszip_t *vfsz = (vfszip_t*)file;
if (vfsz->defer)
return VFS_TELL(vfsz->defer);
return vfsz->pos;
}
unsigned long VFSZIP_GetLen (struct vfsfile_s *file)
{
vfszip_t *vfsz = (vfszip_t*)file;
return vfsz->length;
}
void VFSZIP_Close (struct vfsfile_s *file)
{
vfszip_t *vfsz = (vfszip_t*)file;
if (vfsz->parent->currentfile == file)
vfsz->parent->currentfile = NULL; //make it not us
if (vfsz->defer)
VFS_CLOSE(vfsz->defer);
FSZIP_ClosePath(vfsz->parent);
Z_Free(vfsz);
}
vfsfile_t *FSZIP_OpenVFS(void *handle, flocation_t *loc, const char *mode)
{
int rawofs;
zipfile_t *zip = handle;
vfszip_t *vfsz;
if (strcmp(mode, "rb"))
return NULL; //urm, unable to write/append
if (loc->len < 0)
return NULL;
vfsz = Z_Malloc(sizeof(vfszip_t));
vfsz->parent = zip;
vfsz->index = loc->index;
vfsz->startpos = zip->files[loc->index].filepos;
vfsz->length = loc->len;
#ifdef _DEBUG
Q_strncpyz(vfsz->funcs.dbgname, zip->files[loc->index].name, sizeof(vfsz->funcs.dbgname));
#endif
vfsz->funcs.Close = VFSZIP_Close;
vfsz->funcs.GetLen = VFSZIP_GetLen;
vfsz->funcs.ReadBytes = VFSZIP_ReadBytes;
vfsz->funcs.Seek = VFSZIP_Seek;
vfsz->funcs.Tell = VFSZIP_Tell;
vfsz->funcs.WriteBytes = NULL;
vfsz->funcs.seekingisabadplan = true;
unzLocateFileMy(vfsz->parent->handle, vfsz->index, vfsz->startpos);
rawofs = unzGetCurrentFileUncompressedPos(zip->handle);
vfsz->iscompressed = rawofs<0;
if (!vfsz->iscompressed)
{
vfsz->startpos = rawofs;
VFS_SEEK(zip->raw, vfsz->startpos);
vfsz->parent->currentfile = (vfsfile_t*)vfsz;
}
else if (!ZLIB_LOADED())
{
Z_Free(vfsz);
return NULL;
}
else if (!VFSZIP_MakeActive(vfsz)) /*this is called purely as a test*/
{
/*
windows explorer tends to use deflate64 on large files, which zlib and thus we, do not support, thus this is a 'common' failure path
this might also trigger from other errors, of course.
*/
Z_Free(vfsz);
return NULL;
}
zip->references++;
return (vfsfile_t*)vfsz;
}
searchpathfuncs_t zipfilefuncs = {
FSZIP_GetDisplayPath,
FSZIP_ClosePath,
FSZIP_BuildHash,
FSZIP_FLocate,
FSZIP_ReadFile,
FSZIP_EnumerateFiles,
FSZIP_LoadZipFile,
FSZIP_GeneratePureCRC,
FSZIP_OpenVFS
};
#endif