From: divverent Date: Thu, 8 Jan 2009 18:58:13 +0000 (+0000) Subject: Blub's "deflate" extension to the download system. X-Git-Tag: xonotic-v0.1.0preview~1954 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=2712912e8219e96a09a172b87ec2f4f6cff5ad97;p=xonotic%2Fdarkplaces.git 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 --- 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)