From 2712912e8219e96a09a172b87ec2f4f6cff5ad97 Mon Sep 17 00:00:00 2001 From: divverent Date: Thu, 8 Jan 2009 18:58:13 +0000 Subject: [PATCH] Blub's "deflate" extension to the download system. Currently only provides csprogs.dat in deflated form. Compatible in both directions, compression is only done if both client and server use this new code. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8638 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_parse.c | 85 +++++++++++------ client.h | 1 + fs.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs.h | 5 + server.h | 4 + sv_main.c | 149 ++++++++++++++++++++++++------ 6 files changed, 449 insertions(+), 56 deletions(-) diff --git a/cl_parse.c b/cl_parse.c index 4aa25c5a..55b832ef 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -995,7 +995,10 @@ void CL_BeginDownloads(qboolean aborteddownload) && !FS_FileExists(va("dlcache/%s.%i.%i", csqc_progname.string, csqc_progsize.integer, csqc_progcrc.integer))) { Con_Printf("Downloading new CSQC code to dlcache/%s.%i.%i\n", csqc_progname.string, csqc_progsize.integer, csqc_progcrc.integer); - Cmd_ForwardStringToServer(va("download %s", csqc_progname.string)); + if(cl_serverextension_download.integer >= 2 && FS_HasZlib()) + Cmd_ForwardStringToServer(va("download %s deflate", csqc_progname.string)); + else + Cmd_ForwardStringToServer(va("download %s", csqc_progname.string)); return; } } @@ -1184,37 +1187,57 @@ void CL_StopDownload(int size, int crc) size_t existingsize; const char *extension; - // finished file - // save to disk only if we don't already have it - // (this is mainly for playing back demos) - existingcrc = FS_CRCFile(cls.qw_downloadname, &existingsize); - if (existingsize || gamemode == GAME_NEXUIZ || !strcmp(cls.qw_downloadname, csqc_progname.string)) - // let csprogs ALWAYS go to dlcache, to prevent "viral csprogs"; also, never put files outside dlcache for Nexuiz + if(cls.qw_download_deflate) + { + unsigned char *out; + size_t inflated_size; + out = FS_Inflate(cls.qw_downloadmemory, cls.qw_downloadmemorycursize, &inflated_size, tempmempool); + Mem_Free(cls.qw_downloadmemory); + Con_Printf("Inflated download: new size: %u (%g%%)\n", (unsigned)inflated_size, 100.0 - 100.0*(cls.qw_downloadmemorycursize / (float)inflated_size)); + cls.qw_downloadmemory = out; + cls.qw_downloadmemorycursize = inflated_size; + } + + if(!cls.qw_downloadmemory) { - if ((int)existingsize != size || existingcrc != crc) + Con_Printf("Download \"%s\" is corrupt (see above!)\n", cls.qw_downloadname); + } + else + { + crc = CRC_Block(cls.qw_downloadmemory, cls.qw_downloadmemorycursize); + size = cls.qw_downloadmemorycursize; + // finished file + // save to disk only if we don't already have it + // (this is mainly for playing back demos) + existingcrc = FS_CRCFile(cls.qw_downloadname, &existingsize); + if (existingsize || gamemode == GAME_NEXUIZ || !strcmp(cls.qw_downloadname, csqc_progname.string)) + // let csprogs ALWAYS go to dlcache, to prevent "viral csprogs"; also, never put files outside dlcache for Nexuiz { - // we have a mismatching file, pick another name for it - char name[MAX_QPATH*2]; - dpsnprintf(name, sizeof(name), "dlcache/%s.%i.%i", cls.qw_downloadname, size, crc); - if (!FS_FileExists(name)) + if ((int)existingsize != size || existingcrc != crc) { - Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", name, size, crc); - FS_WriteFile(name, cls.qw_downloadmemory, cls.qw_downloadmemorycursize); + // we have a mismatching file, pick another name for it + char name[MAX_QPATH*2]; + dpsnprintf(name, sizeof(name), "dlcache/%s.%i.%i", cls.qw_downloadname, size, crc); + if (!FS_FileExists(name)) + { + Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", name, size, crc); + FS_WriteFile(name, cls.qw_downloadmemory, cls.qw_downloadmemorycursize); + } } } - } - else - { - // we either don't have it or have a mismatching file... - // so it's time to accept the file - // but if we already have a mismatching file we need to rename - // this new one, and if we already have this file in renamed form, - // we do nothing - Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", cls.qw_downloadname, size, crc); - FS_WriteFile(cls.qw_downloadname, cls.qw_downloadmemory, cls.qw_downloadmemorycursize); - extension = FS_FileExtension(cls.qw_downloadname); - if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3")) - FS_Rescan(); + else + { + // we either don't have it or have a mismatching file... + // so it's time to accept the file + // but if we already have a mismatching file we need to rename + // this new one, and if we already have this file in renamed form, + // we do nothing + Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", cls.qw_downloadname, size, crc); + FS_WriteFile(cls.qw_downloadname, cls.qw_downloadmemory, cls.qw_downloadmemorycursize); + extension = FS_FileExtension(cls.qw_downloadname); + if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3")) + FS_Rescan(); + } } } else if (cls.qw_downloadmemory && size) @@ -1293,6 +1316,14 @@ void CL_DownloadBegin_f(void) cls.qw_downloadmemory = Mem_Alloc(cls.permanentmempool, cls.qw_downloadmemorymaxsize); cls.qw_downloadnumber++; + cls.qw_download_deflate = false; + if(Cmd_Argc() >= 4) + { + if(!strcmp(Cmd_Argv(3), "deflate")) + cls.qw_download_deflate = true; + // check further encodings here + } + Cmd_ForwardStringToServer("sv_startdownload"); } diff --git a/client.h b/client.h index 889f8e73..6c3f46ac 100644 --- a/client.h +++ b/client.h @@ -589,6 +589,7 @@ typedef struct client_static_s double qw_downloadspeedtime; int qw_downloadspeedcount; int qw_downloadspeedrate; + qboolean qw_download_deflate; // current file upload buffer (for uploading screenshots to server) unsigned char *qw_uploaddata; diff --git a/fs.c b/fs.c index 4f7baf9d..119499f9 100644 --- a/fs.c +++ b/fs.c @@ -111,8 +111,23 @@ CONSTANTS #define MAX_WBITS 15 #define Z_OK 0 #define Z_STREAM_END 1 +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) #define ZLIB_VERSION "1.2.3" +#define Z_BINARY 0 +#define Z_DEFLATED 8 +#define Z_MEMLEVEL_DEFAULT 8 + +#define Z_NULL 0 +#define Z_DEFAULT_COMPRESSION (-1) +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + // Uncomment the following line if the zlib DLL you have still uses // the 1.1.x series calling convention on Win32 (WINAPI) //#define ZLIB_USES_WINAPI @@ -156,6 +171,8 @@ typedef struct #define QFILE_FLAG_PACKED (1 << 0) // file is compressed using the deflate algorithm (PK3 only) #define QFILE_FLAG_DEFLATED (1 << 1) +// file is actually already loaded data +#define QFILE_FLAG_DATA (1 << 2) #define FILE_BUFF_SIZE 2048 typedef struct @@ -182,6 +199,9 @@ struct qfile_s // For zipped files ztoolkit_t* ztk; + + // for data files + const unsigned char *data; }; @@ -316,9 +336,16 @@ static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush); static int (ZEXPORT *qz_inflateEnd) (z_stream* strm); static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size); static int (ZEXPORT *qz_inflateReset) (z_stream* strm); +static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size); +static int (ZEXPORT *qz_deflateEnd) (z_stream* strm); +static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush); #define qz_inflateInit2(strm, windowBits) \ qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream)) + +// qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) static dllfunction_t zlibfuncs[] = { @@ -326,6 +353,9 @@ static dllfunction_t zlibfuncs[] = {"inflateEnd", (void **) &qz_inflateEnd}, {"inflateInit2_", (void **) &qz_inflateInit2_}, {"inflateReset", (void **) &qz_inflateReset}, + {"deflateInit2_", (void **) &qz_deflateInit2_}, + {"deflateEnd", (void **) &qz_deflateEnd}, + {"deflate", (void **) &qz_deflate}, {NULL, NULL} }; @@ -392,6 +422,18 @@ qboolean PK3_OpenLibrary (void) return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs); } +/* +==================== +FS_HasZlib + +See if zlib is available +==================== +*/ +qboolean FS_HasZlib(void) +{ + PK3_OpenLibrary(); // to be safe + return (zlib_dll != 0); +} /* ==================== @@ -2036,6 +2078,25 @@ qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet) } +/* +==================== +FS_FileFromData + +Open a file. The syntax is the same as fopen +==================== +*/ +qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet) +{ + qfile_t* file; + file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file)); + memset (file, 0, sizeof (*file)); + file->flags = QFILE_FLAG_DATA; + file->ungetc = EOF; + file->real_length = size; + file->data = data; + return file; +} + /* ==================== FS_Close @@ -2045,6 +2106,12 @@ Close a file */ int FS_Close (qfile_t* file) { + if(file->flags & QFILE_FLAG_DATA) + { + Mem_Free(file); + return 0; + } + if (close (file->handle)) return EOF; @@ -2115,6 +2182,16 @@ fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize) else done = 0; + if(file->flags & QFILE_FLAG_DATA) + { + size_t left = file->real_length - file->position; + if(buffersize > left) + buffersize = left; + memcpy(buffer, file->data + file->position, buffersize); + file->position += buffersize; + return buffersize; + } + // First, we copy as many bytes as we can from "buff" if (file->buff_ind < file->buff_len) { @@ -2396,6 +2473,12 @@ int FS_Seek (qfile_t* file, fs_offset_t offset, int whence) if (offset < 0 || offset > file->real_length) return -1; + if(file->flags & QFILE_FLAG_DATA) + { + file->position = offset; + return 0; + } + // If we have the data in our read buffer, we don't need to actually seek if (file->position - file->buff_len <= offset && offset <= file->position) { @@ -3102,3 +3185,181 @@ int FS_CRCFile(const char *filename, size_t *filesizepointer) return crc; } +unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool) +{ + z_stream strm; + unsigned char *out = NULL; + unsigned char *tmp; + + memset(&strm, 0, sizeof(strm)); + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + if(level < 0) + level = Z_DEFAULT_COMPRESSION; + + if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK) + { + Con_Printf("FS_Deflate: deflate init error!\n"); + return NULL; + } + + strm.next_in = (unsigned char*)data; + strm.avail_in = size; + + tmp = Mem_Alloc(tempmempool, size); + if(!tmp) + { + Con_Printf("FS_Deflate: not enough memory in tempmempool!\n"); + qz_deflateEnd(&strm); + return NULL; + } + + strm.next_out = tmp; + strm.avail_out = size; + + if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END) + { + Con_Printf("FS_Deflate: deflate failed!\n"); + qz_deflateEnd(&strm); + Mem_Free(tmp); + return NULL; + } + + if(qz_deflateEnd(&strm) != Z_OK) + { + Con_Printf("FS_Deflate: deflateEnd failed\n"); + Mem_Free(tmp); + return NULL; + } + + if(strm.total_out >= size) + { + Con_Printf("FS_Deflate: deflate is useless on this data!\n"); + Mem_Free(tmp); + return NULL; + } + + out = Mem_Alloc(mempool, strm.total_out); + if(!out) + { + Con_Printf("FS_Deflate: not enough memory in target mempool!\n"); + Mem_Free(tmp); + return NULL; + } + + if(deflated_size) + *deflated_size = (size_t)strm.total_out; + + memcpy(out, tmp, strm.total_out); + Mem_Free(tmp); + + return out; +} + +static void AssertBufsize(sizebuf_t *buf, int length) +{ + if(buf->cursize + length > buf->maxsize) + { + int oldsize = buf->maxsize; + unsigned char *olddata; + olddata = buf->data; + buf->maxsize += length; + buf->data = Mem_Alloc(tempmempool, buf->maxsize); + if(olddata) + { + memcpy(buf->data, olddata, oldsize); + Mem_Free(olddata); + } + } +} + +unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool) +{ + int ret; + z_stream strm; + unsigned char *out = NULL; + unsigned char tmp[2048]; + unsigned int have; + sizebuf_t outbuf; + + memset(&outbuf, 0, sizeof(outbuf)); + outbuf.data = Mem_Alloc(tempmempool, sizeof(tmp)); + outbuf.maxsize = sizeof(tmp); + + memset(&strm, 0, sizeof(strm)); + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK) + { + Con_Printf("FS_Inflate: inflate init error!\n"); + Mem_Free(outbuf.data); + return NULL; + } + + strm.next_in = (unsigned char*)data; + strm.avail_in = size; + + do + { + strm.next_out = tmp; + strm.avail_out = sizeof(tmp); + ret = qz_inflate(&strm, Z_NO_FLUSH); + // it either returns Z_OK on progress, Z_STREAM_END on end + // or an error code + switch(ret) + { + case Z_STREAM_END: + case Z_OK: + break; + + case Z_STREAM_ERROR: + Con_Print("FS_Inflate: stream error!\n"); + break; + case Z_DATA_ERROR: + Con_Print("FS_Inflate: data error!\n"); + break; + case Z_MEM_ERROR: + Con_Print("FS_Inflate: mem error!\n"); + break; + case Z_BUF_ERROR: + Con_Print("FS_Inflate: buf error!\n"); + break; + default: + Con_Print("FS_Inflate: unknown error!\n"); + break; + + } + if(ret != Z_OK && ret != Z_STREAM_END) + { + Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in); + Mem_Free(outbuf.data); + qz_inflateEnd(&strm); + return NULL; + } + have = sizeof(tmp) - strm.avail_out; + AssertBufsize(&outbuf, max(have, sizeof(tmp))); + SZ_Write(&outbuf, tmp, have); + } while(ret != Z_STREAM_END); + + qz_inflateEnd(&strm); + + out = Mem_Alloc(mempool, outbuf.cursize); + if(!out) + { + Con_Printf("FS_Inflate: not enough memory in target mempool!\n"); + Mem_Free(outbuf.data); + return NULL; + } + + memcpy(out, outbuf.data, outbuf.cursize); + Mem_Free(outbuf.data); + + if(inflated_size) + *inflated_size = (size_t)outbuf.cursize; + + return out; +} diff --git a/fs.h b/fs.h index cf2290fa..9f01461d 100644 --- a/fs.h +++ b/fs.h @@ -59,6 +59,7 @@ qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep const char *FS_WhichPack(const char *filename); qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet); qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet); +qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet); int FS_Close (qfile_t* file); fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize); fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize); @@ -112,5 +113,9 @@ qboolean FS_SysFileExists (const char *filename); // only look for files outside void FS_mkdir (const char *path); +unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool); +unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool); + +qboolean FS_HasZlib(void); #endif diff --git a/server.h b/server.h index fe11c266..0307bb52 100644 --- a/server.h +++ b/server.h @@ -86,6 +86,9 @@ typedef struct server_s int csqc_progcrc; // -1 = no progs int csqc_progsize; // -1 = no progs char csqc_progname[MAX_QPATH]; // copied from csqc_progname at level start + unsigned char *csqc_progdata; + size_t csqc_progsize_deflated; + unsigned char *csqc_progdata_deflated; // collision culling data world_t world; @@ -270,6 +273,7 @@ typedef struct client_s int download_expectedposition; // next position the client should ack qboolean download_started; char download_name[MAX_QPATH]; + qboolean download_deflate; // fixangle data qboolean fixangle_angles_set; diff --git a/sv_main.c b/sv_main.c index 9ca62e2c..e0fd7e76 100644 --- a/sv_main.c +++ b/sv_main.c @@ -788,24 +788,15 @@ void SV_SendServerinfo (client_t *client) if(client->sv_demo_file != NULL) { - void *csqcbuf; - fs_offset_t csqclen; - int csqccrc; int i; char buf[NET_MAXMESSAGE]; sizebuf_t sb; - csqcbuf = FS_LoadFile(sv.csqc_progname, tempmempool, true, &csqclen); - if(csqcbuf) - { - csqccrc = CRC_Block(csqcbuf, csqclen); - sb.data = (void *) buf; - sb.maxsize = sizeof(buf); - i = 0; - while(MakeDownloadPacket(sv.csqc_progname, csqcbuf, csqclen, csqccrc, i++, &sb, sv.protocol)) - SV_WriteDemoMessage(client, &sb, false); - Mem_Free(csqcbuf); - } + sb.data = (void *) buf; + sb.maxsize = sizeof(buf); + i = 0; + while(MakeDownloadPacket(sv.csqc_progname, sv.csqc_progdata, sv.csqc_progsize, sv.csqc_progcrc, i++, &sb, sv.protocol)) + SV_WriteDemoMessage(client, &sb, false); } //[515]: init stufftext string (it is sent before svc_serverinfo) @@ -817,10 +808,12 @@ void SV_SendServerinfo (client_t *client) } } - if (sv_allowdownloads.integer) + //if (sv_allowdownloads.integer) + // always send the info that the server supports the protocol, even if downloads are forbidden + // only because of that, the CSQC exception can work { MSG_WriteByte (&client->netconnection->message, svc_stufftext); - MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1\n"); + MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 2\n"); } // send at this time so it's guaranteed to get executed at the right time @@ -2128,13 +2121,33 @@ static void SV_StartDownload_f(void) host_client->download_started = true; } +static void Download_CheckExtensions(void) +{ + int i; + int argc = Cmd_Argc(); + + // first reset them all + host_client->download_deflate = false; + + for(i = 2; i < argc; ++i) + { + if(!strcmp(Cmd_Argv(i), "deflate")) + { + host_client->download_deflate = true; + break; + } + } +} + static void SV_Download_f(void) { const char *whichpack, *whichpack2, *extension; + qboolean is_csqc; // so we need to check only once - if (Cmd_Argc() != 2) + if (Cmd_Argc() < 2) { - SV_ClientPrintf("usage: download \n"); + SV_ClientPrintf("usage: download {}*\n"); + SV_ClientPrintf(" supported extensions: deflate\n"); return; } @@ -2158,13 +2171,17 @@ static void SV_Download_f(void) host_client->download_started = false; } - if (!sv_allowdownloads.integer) + is_csqc = (sv.csqc_progname[0] && strcmp(Cmd_Argv(1), sv.csqc_progname) == 0); + + if (!sv_allowdownloads.integer && !is_csqc) { SV_ClientPrintf("Downloads are disabled on this server\n"); Host_ClientCommands("\nstopdownload\n"); return; } + Download_CheckExtensions(); + strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name)); extension = FS_FileExtension(host_client->download_name); @@ -2172,6 +2189,30 @@ static void SV_Download_f(void) if (developer.integer >= 100) Con_Printf("Download request for %s by %s\n", host_client->download_name, host_client->name); + if(is_csqc) + { + char extensions[MAX_QPATH]; // make sure this can hold all extensions + extensions[0] = '\0'; + + if(host_client->download_deflate) + strlcat(extensions, " deflate", sizeof(extensions)); + + Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name); + + if(host_client->download_deflate) + host_client->download_file = FS_FileFromData(sv.csqc_progdata_deflated, sv.csqc_progsize_deflated, true); + else + host_client->download_file = FS_FileFromData(sv.csqc_progdata, sv.csqc_progsize, true); + + // no, no space is needed between %s and %s :P + Host_ClientCommands("\ncl_downloadbegin %i %s%s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name, extensions); + + host_client->download_expectedposition = 0; + host_client->download_started = false; + host_client->sendsignon = true; // make sure this message is sent + return; + } + if (!FS_FileExists(host_client->download_name)) { SV_ClientPrintf("Download rejected: server does not have the file \"%s\"\nYou may need to separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name); @@ -2259,6 +2300,20 @@ static void SV_Download_f(void) Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name); + /* + * we can only do this if we would actually deflate on the fly + * which we do not (yet)! + { + char extensions[MAX_QPATH]; // make sure this can hold all extensions + extensions[0] = '\0'; + + if(host_client->download_deflate) + strlcat(extensions, " deflate", sizeof(extensions)); + + // no, no space is needed between %s and %s :P + Host_ClientCommands("\ncl_downloadbegin %i %s%s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name, extensions); + } + */ Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name); host_client->download_expectedposition = 0; @@ -2537,6 +2592,51 @@ static void SV_CreateBaseline (void) } } +/* +================ +SV_Prepare_CSQC + +Load csprogs.dat and comperss it so it doesn't need to be +reloaded on request. +================ +*/ +void SV_Prepare_CSQC(void) +{ + fs_offset_t progsize; + + if(sv.csqc_progdata) + { + Con_DPrintf("Unloading old CSQC data.\n"); + Mem_Free(sv.csqc_progdata); + if(sv.csqc_progdata_deflated) + Mem_Free(sv.csqc_progdata_deflated); + } + + sv.csqc_progdata = NULL; + sv.csqc_progdata_deflated = NULL; + + Con_Print("Loading csprogs.dat\n"); + + sv.csqc_progname[0] = 0; + sv.csqc_progdata = FS_LoadFile(csqc_progname.string, sv_mempool, false, &progsize); + + if(progsize > 0) + { + size_t deflated_size; + + sv.csqc_progsize = (int)progsize; + sv.csqc_progcrc = CRC_Block(sv.csqc_progdata, progsize); + strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname)); + Con_Printf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc); + + Con_Print("Compressing csprogs.dat\n"); + //unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool); + sv.csqc_progdata_deflated = FS_Deflate(sv.csqc_progdata, progsize, &deflated_size, -1, sv_mempool); + sv.csqc_progsize_deflated = (int)deflated_size; + Con_Printf("Deflated: %g%%\n", 100.0 - 100.0 * (deflated_size / (float)progsize)); + Con_DPrintf("Uncompressed: %u\nCompressed: %u\n", (unsigned)sv.csqc_progsize, (unsigned)sv.csqc_progsize_deflated); + } +} /* ================ @@ -2980,7 +3080,6 @@ static qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent) static void SV_VM_Setup(void) { extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat - size_t csprogsdatasize; PRVM_Begin; PRVM_InitProg( PRVM_SERVERPROG ); @@ -3054,15 +3153,7 @@ static void SV_VM_Setup(void) PRVM_End; - // see if there is a csprogs.dat installed, and if so, set the csqc_progcrc accordingly, this will be sent to connecting clients to tell them to only load a matching csprogs.dat file - sv.csqc_progname[0] = 0; - sv.csqc_progcrc = FS_CRCFile(csqc_progname.string, &csprogsdatasize); - sv.csqc_progsize = csprogsdatasize; - if (sv.csqc_progsize > 0) - { - strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname)); - Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc); - } + SV_Prepare_CSQC(); } void SV_VM_Begin(void) -- 2.39.2