]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/common/vfs.c
Merge branch 'NateEag-master-patch-12920' into 'master'
[xonotic/netradiant.git] / tools / quake3 / common / vfs.c
1 /*
2    Copyright (c) 2001, Loki software, inc.
3    All rights reserved.
4
5    Redistribution and use in source and binary forms, with or without modification,
6    are permitted provided that the following conditions are met:
7
8    Redistributions of source code must retain the above copyright notice, this list
9    of conditions and the following disclaimer.
10
11    Redistributions in binary form must reproduce the above copyright notice, this
12    list of conditions and the following disclaimer in the documentation and/or
13    other materials provided with the distribution.
14
15    Neither the name of Loki software nor the names of its contributors may be used
16    to endorse or promote products derived from this software without specific prior
17    written permission.
18
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22    DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
23    DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 //
32 // Rules:
33 //
34 // - Directories should be searched in the following order: ~/.q3a/baseq3,
35 //   install dir (/usr/local/games/quake3/baseq3) and cd_path (/mnt/cdrom/baseq3).
36 //
37 // - Pak files are searched first inside the directories.
38 // - Case insensitive.
39 // - Unix-style slashes (/) (windows is backwards .. everyone knows that)
40 //
41 // Leonardo Zide (leo@lokigames.com)
42 //
43
44 #include <string.h>
45 #include <stdlib.h>
46 #include <sys/stat.h>
47
48 #include "cmdlib.h"
49 #include "filematch.h"
50 #include "mathlib.h"
51 #include "inout.h"
52 #include "vfs.h"
53 #include <unzip.h>
54 #include <glib.h>
55 #define GARUX_DISABLE_BAD_MINIZ
56 #ifndef GARUX_DISABLE_BAD_MINIZ
57 #include "miniz.h"
58 #endif
59
60 typedef struct
61 {
62         char* unzFilePath;
63         char*   name;
64         unzFile zipfile;
65         unz_file_pos zippos;
66         guint32 size;
67 } VFS_PAKFILE;
68
69 // =============================================================================
70 // Global variables
71
72 static GSList*  g_unzFiles;
73 static GSList*  g_pakFiles;
74 static char g_strDirs[VFS_MAXDIRS][PATH_MAX + 1];
75 static int g_numDirs;
76 char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
77 int g_numForbiddenDirs = 0;
78 static gboolean g_bUsePak = TRUE;
79 char g_strLoadedFileLocation[1024];
80
81 // =============================================================================
82 // Static functions
83
84 static void vfsAddSlash( char *str ){
85         int n = strlen( str );
86         if ( n > 0 ) {
87                 if ( str[n - 1] != '\\' && str[n - 1] != '/' ) {
88                         strcat( str, "/" );
89                 }
90         }
91 }
92
93 static void vfsFixDOSName( char *src ){
94         if ( src == NULL ) {
95                 return;
96         }
97
98         while ( *src )
99         {
100                 if ( *src == '\\' ) {
101                         *src = '/';
102                 }
103                 src++;
104         }
105 }
106
107 //!\todo Define globally or use heap-allocated string.
108 #define NAME_MAX 255
109
110 static void vfsInitPakFile( const char *filename ){
111         unz_global_info gi;
112         unzFile uf;
113         guint32 i;
114         int err;
115
116         uf = unzOpen( filename );
117         if ( uf == NULL ) {
118                 return;
119         }
120
121         g_unzFiles = g_slist_append( g_unzFiles, uf );
122
123         err = unzGetGlobalInfo( uf,&gi );
124         if ( err != UNZ_OK ) {
125                 return;
126         }
127         unzGoToFirstFile( uf );
128
129         char* unzFilePath = strdup( filename );
130
131         for ( i = 0; i < gi.number_entry; i++ )
132         {
133                 char filename_inzip[NAME_MAX];
134                 char *filename_lower;
135                 unz_file_info file_info;
136                 VFS_PAKFILE* file;
137
138                 err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof( filename_inzip ), NULL, 0, NULL, 0 );
139                 if ( err != UNZ_OK ) {
140                         break;
141                 }
142                 unz_file_pos pos;
143                 err = unzGetFilePos( uf, &pos );
144                 if ( err != UNZ_OK ) {
145                         break;
146                 }
147
148                 file = (VFS_PAKFILE*)safe_malloc( sizeof( VFS_PAKFILE ) );
149                 g_pakFiles = g_slist_append( g_pakFiles, file );
150
151                 vfsFixDOSName( filename_inzip );
152                  //-1 null terminated string
153                 filename_lower = g_ascii_strdown( filename_inzip, -1 );
154
155                 file->name = strdup( filename_lower );
156                 file->size = file_info.uncompressed_size;
157                 file->zipfile = uf;
158                 file->unzFilePath = unzFilePath;
159                 file->zippos = pos;
160
161                 if ( ( i + 1 ) < gi.number_entry ) {
162                         err = unzGoToNextFile( uf );
163                         if ( err != UNZ_OK ) {
164                                 break;
165                         }
166                 }
167                 g_free( filename_lower );
168         }
169 }
170
171 // =============================================================================
172 // Global functions
173
174 // reads all pak files from a dir
175 void vfsInitDirectory( const char *path ){
176         char filename[PATH_MAX];
177         char *dirlist;
178         GDir *dir;
179         int j;
180
181         for ( j = 0; j < g_numForbiddenDirs; ++j )
182         {
183                 char* dbuf = g_strdup( path );
184                 if ( *dbuf && dbuf[strlen( dbuf ) - 1] == '/' ) {
185                         dbuf[strlen( dbuf ) - 1] = 0;
186                 }
187                 const char *p = strrchr( dbuf, '/' );
188                 p = ( p ? ( p + 1 ) : dbuf );
189                 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
190                         g_free( dbuf );
191                         break;
192                 }
193                 g_free( dbuf );
194         }
195         if ( j < g_numForbiddenDirs ) {
196                 return;
197         }
198
199         if ( g_numDirs == VFS_MAXDIRS ) {
200                 Sys_FPrintf( SYS_WRN, "WARNING: too many VFS directories, can't init %s\n", path );
201                 return;
202         }
203
204         Sys_Printf( "VFS Init: %s\n", path );
205
206         strncpy( g_strDirs[g_numDirs], path, PATH_MAX );
207         g_strDirs[g_numDirs][PATH_MAX] = 0;
208         vfsFixDOSName( g_strDirs[g_numDirs] );
209         vfsAddSlash( g_strDirs[g_numDirs] );
210         g_numDirs++;
211
212         if ( g_bUsePak ) {
213                 dir = g_dir_open( path, 0, NULL );
214
215                 if ( dir != NULL ) {
216                         while ( 1 )
217                         {
218                                 const char* name = g_dir_read_name( dir );
219                                 if ( name == NULL ) {
220                                         break;
221                                 }
222
223                                 for ( j = 0; j < g_numForbiddenDirs; ++j )
224                                 {
225                                         const char *p = strrchr( name, '/' );
226                                         p = ( p ? ( p + 1 ) : name );
227                                         if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
228                                                 break;
229                                         }
230                                 }
231                                 if ( j < g_numForbiddenDirs ) {
232                                         continue;
233                                 }
234
235                                 dirlist = g_strdup( name );
236
237                                 {
238                                         char *ext = strrchr( dirlist, '.' );
239
240                                         if ( ext != NULL && ( !Q_stricmp( ext, ".pk3dir" ) || !Q_stricmp( ext, ".dpkdir" ) ) ) {
241                                                 if ( g_numDirs == VFS_MAXDIRS ) {
242                                                         g_free( dirlist );
243                                                         continue;
244                                                 }
245                                                 snprintf( g_strDirs[g_numDirs], PATH_MAX, "%s/%s", path, name );
246                                                 g_strDirs[g_numDirs][PATH_MAX-1] = '\0';
247                                                 vfsFixDOSName( g_strDirs[g_numDirs] );
248                                                 vfsAddSlash( g_strDirs[g_numDirs] );
249                                                 ++g_numDirs;
250                                         }
251
252                                         if ( ext == NULL || ( Q_stricmp( ext, ".pk3" ) != 0 && Q_stricmp( ext, ".dpk" ) != 0 ) ) {
253                                                 g_free( dirlist );
254                                                 continue;
255                                         }
256                                 }
257
258                                 sprintf( filename, "%s/%s", path, dirlist );
259                                 vfsInitPakFile( filename );
260
261                                 g_free( dirlist );
262                         }
263                         g_dir_close( dir );
264                 }
265         }
266 }
267
268
269 // lists all .shader files
270 void vfsListShaderFiles( char* list, int *num ){
271         //char filename[PATH_MAX];
272         char *dirlist;
273         GDir *dir;
274         int i, k;
275         char path[NAME_MAX];
276 /* search in dirs */
277         for ( i = 0; i < g_numDirs; i++ ){
278                 strncpy( path, g_strDirs[ i ], NAME_MAX );
279                 strcat( path, "scripts/" );
280
281                 dir = g_dir_open( path, 0, NULL );
282
283                 if ( dir != NULL ) {
284                         while ( 1 )
285                         {
286                                 const char* name = g_dir_read_name( dir );
287                                 if ( name == NULL ) {
288                                         break;
289                                 }
290                                 dirlist = g_strdup( name );
291                                 char *ext = strrchr( dirlist, '.' );
292
293                                 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
294                                         continue;
295                                 }
296
297                                 for ( k = 0; k < *num; k++ ){
298                                         if ( !Q_stricmp( list + k*65, dirlist ) ) goto shISdouplicate;
299                                 }
300                                 strcpy( list + (*num)*65, dirlist );
301                                 (*num)++;
302 shISdouplicate:
303                                 g_free( dirlist );
304                         }
305                         g_dir_close( dir );
306                 }
307         }
308         /* search in packs */
309         GSList *lst;
310
311         for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
312         {
313                 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
314
315                 char *ext = strrchr( file->name, '.' );
316
317                 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
318                         continue;
319                 }
320                 //name + ext this time
321                 ext = strrchr( file->name, '/' );
322                 ext++;
323
324                 for ( k = 0; k < *num; k++ ){
325                         if ( !Q_stricmp( list + k*65, ext ) ) goto shISdouplicate2;
326                 }
327                 strcpy( list + (*num)*65, ext );
328                 (*num)++;
329 shISdouplicate2:
330                 continue;
331         }
332 }
333
334 // frees all memory that we allocated
335 void vfsShutdown(){
336         while ( g_unzFiles )
337         {
338                 unzClose( (unzFile)g_unzFiles->data );
339                 g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
340         }
341
342         while ( g_pakFiles )
343         {
344                 VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
345                 free( file->unzFilePath );
346                 free( file->name );
347                 free( file );
348                 g_pakFiles = g_slist_remove( g_pakFiles, file );
349         }
350 }
351
352 // return the number of files that match
353 int vfsGetFileCount( const char *filename ){
354         int i, count = 0;
355         char fixed[NAME_MAX], tmp[NAME_MAX];
356         char *lower;
357         GSList *lst;
358
359         strcpy( fixed, filename );
360         vfsFixDOSName( fixed );
361         lower = g_ascii_strdown( fixed, -1 );
362
363         for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
364         {
365                 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
366
367                 if ( strcmp( file->name, lower ) == 0 ) {
368                         count++;
369                 }
370         }
371
372         for ( i = 0; i < g_numDirs; i++ )
373         {
374                 strcpy( tmp, g_strDirs[i] );
375                 strcat( tmp, lower );
376                 if ( access( tmp, R_OK ) == 0 ) {
377                         count++;
378                 }
379         }
380         g_free( lower );
381         return count;
382 }
383
384 static qboolean isSymlink(const unz_file_info64 *fileInfo) {
385         // see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/stat.h
386         // redefine so it works outside of Unices
387         const unsigned long Q3MAP_S_IFMT = 00170000;
388         const unsigned long Q3MAP_S_IFLNK = 0120000;
389         // see https://trac.edgewall.org/attachment/ticket/8919/ZipDownload.patch
390         const unsigned long PKZIP_EXTERNAL_ATTR_FILE_TYPE_SHIFT = 16;
391
392         unsigned long attr = fileInfo->external_fa >> PKZIP_EXTERNAL_ATTR_FILE_TYPE_SHIFT;
393         return (attr & Q3MAP_S_IFMT) == Q3MAP_S_IFLNK;
394 }
395
396 // The zip format has a maximum filename size of 64K
397 static const int MAX_FILENAME_BUF = 65537;
398
399 /* The symlink implementation is ported from Dæmon engine implementation by slipher which was a complete rewrite of one illwieckz did on Dæmon by taking inspiration from Darkplaces engine.
400
401 See:
402
403 - https://github.com/DaemonEngine/Daemon/blob/master/src/common/FileSystem.cpp
404 - https://gitlab.com/xonotic/darkplaces/-/blob/div0-stable/fs.c
405
406 Some words by slipher:
407
408 > Symlinks are a bad feature which you should not use. Therefore, the implementation is as
409 > slow as possible with a full iteration of the archive performed for each symlink.
410
411 > The symlink path `relative` must be relative to the symlink's location.
412 > Only supports paths consisting of "../" 0 or more times, followed by non-magical path components.
413 */
414 void resolveSymlinkPath( const char* base, const char* relative, char* resolved ){
415
416         base = g_path_get_dirname( base );
417
418         while( g_str_has_prefix( relative, "../" ) )
419         {
420                 if ( base[0] == '\0' )
421                 {
422                         Sys_FPrintf( SYS_WRN, "Error while reading symbolic link: \"%s\": no such directory\n", base );
423                         resolved[0] = '\0';
424                         return;
425                 }
426
427                 base = g_path_get_dirname( base );
428                 relative += 3;
429         }
430
431         snprintf( resolved, MAX_FILENAME_BUF, "%s/%s", base, relative);
432 }
433
434 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0
435 int vfsLoadFile( const char *filename, void **bufferptr, int index ){
436         int i, count = 0;
437         char tmp[NAME_MAX], fixed[NAME_MAX];
438         char *lower;
439         GSList *lst;
440
441         // filename is a full path
442         if ( index == -1 ) {
443                 strcpy( g_strLoadedFileLocation, filename );
444                 long len;
445                 FILE *f;
446
447                 f = fopen( filename, "rb" );
448                 if ( f == NULL ) {
449                         return -1;
450                 }
451
452                 fseek( f, 0, SEEK_END );
453                 len = ftell( f );
454                 rewind( f );
455
456                 *bufferptr = safe_malloc( len + 1 );
457                 if ( *bufferptr == NULL ) {
458                         fclose( f );
459                         return -1;
460                 }
461
462                 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
463                         fclose( f );
464                         return -1;
465                 }
466                 fclose( f );
467
468                 // we need to end the buffer with a 0
469                 ( (char*) ( *bufferptr ) )[len] = 0;
470
471                 return len;
472         }
473
474         *bufferptr = NULL;
475         strncpy( fixed, filename, sizeof( fixed ) );
476         vfsFixDOSName( fixed );
477         lower = g_ascii_strdown( fixed, -1 );
478
479         for ( i = 0; i < g_numDirs; i++ )
480         {
481                 strcpy( tmp, g_strDirs[i] );
482                 strcat( tmp, filename );
483                 if ( access( tmp, R_OK ) == 0 ) {
484                         if ( count == index ) {
485                                 strcpy( g_strLoadedFileLocation, tmp );
486
487                                 long len;
488                                 FILE *f;
489
490                                 f = fopen( tmp, "rb" );
491                                 if ( f == NULL ) {
492                                         return -1;
493                                 }
494
495                                 fseek( f, 0, SEEK_END );
496                                 len = ftell( f );
497                                 rewind( f );
498
499                                 *bufferptr = safe_malloc( len + 1 );
500                                 if ( *bufferptr == NULL ) {
501                                         fclose( f );
502                                         return -1;
503                                 }
504
505                                 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
506                                         fclose( f );
507                                         return -1;
508                                 }
509                                 fclose( f );
510
511                                 // we need to end the buffer with a 0
512                                 ( (char*) ( *bufferptr ) )[len] = 0;
513
514                                 return len;
515                         }
516
517                         count++;
518                 }
519         }
520
521         // Do not resolve more than 5 recursive symbolic links to
522         // prevent circular symbolic links.
523         int max_symlink_depth = 5;
524
525         openSymlinkTarget:
526         for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
527         {
528                 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
529
530                 if ( strcmp( file->name, lower ) != 0 ) {
531                         continue;
532                 }
533
534                 if ( count == index ) {
535                         strcpy( g_strLoadedFileLocation, file->unzFilePath );
536                         strcat( g_strLoadedFileLocation, " :: " );
537                         strcat( g_strLoadedFileLocation, filename );
538
539
540                 if ( unzGoToFilePos( file->zipfile, &file->zippos ) != UNZ_OK ) {
541                         return -1;
542                 }
543                         if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
544                                 return -1;
545                         }
546
547                         unz_file_info64 fileInfo;
548                         if ( unzGetCurrentFileInfo64( file->zipfile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0 ) != UNZ_OK ) {
549                                 return -1;
550                         }
551
552                         *bufferptr = safe_malloc( file->size + 1 );
553                         // we need to end the buffer with a 0
554                         ( (char*) ( *bufferptr ) )[file->size] = 0;
555
556                         i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
557                         unzCloseCurrentFile( file->zipfile );
558
559                         if ( isSymlink( &fileInfo ) ) {
560                                 Sys_FPrintf( SYS_VRB, "Found symbolic link: \"%s\"\n", filename );
561
562                                 if ( max_symlink_depth == 0 ) {
563                                         Sys_FPrintf( SYS_WRN, "Maximum symbolic link depth reached\n" );
564                                         g_free( lower );
565                                         return -1;
566                                 }
567
568                                 max_symlink_depth--;
569
570                                 const char* relative = (const char*) *bufferptr;
571                                 char resolved[MAX_FILENAME_BUF];
572
573                                 resolveSymlinkPath( file->name, relative, resolved );
574
575                                 Sys_FPrintf( SYS_VRB, "Resolved symbolic link: \"%s\"\n", resolved );
576
577                                 g_free( lower );
578                                 strncpy( fixed, resolved, sizeof( fixed ) );
579                                 vfsFixDOSName( fixed );
580                                 lower = g_ascii_strdown( fixed, -1 );
581
582                                 // slow as possible full iteration of the archive
583                                 goto openSymlinkTarget;
584                         }
585
586                         if ( i < 0 ) {
587                                 g_free( lower );
588                                 return -1;
589                         }
590                         else{
591                                 g_free( lower );
592                                 return file->size;
593                         }
594                 }
595
596                 count++;
597         }
598
599         g_free( lower );
600         return -1;
601 }
602
603
604 qboolean vfsPackFile( const char *filename, const char *packname, const int compLevel ){
605 #ifndef GARUX_DISABLE_BAD_MINIZ
606         int i;
607         char tmp[NAME_MAX], fixed[NAME_MAX];
608         GSList *lst;
609
610         byte *bufferptr = NULL;
611         strcpy( fixed, filename );
612         vfsFixDOSName( fixed );
613         g_strdown( fixed );
614
615         for ( i = 0; i < g_numDirs; i++ )
616         {
617                 strcpy( tmp, g_strDirs[i] );
618                 strcat( tmp, filename );
619                 if ( access( tmp, R_OK ) == 0 ) {
620                         if ( access( packname, R_OK ) == 0 ) {
621                                 mz_zip_archive zip;
622                                 memset( &zip, 0, sizeof(zip) );
623                                 mz_zip_reader_init_file( &zip, packname, 0 );
624                                 mz_zip_writer_init_from_reader( &zip, packname );
625
626                                 mz_bool success = MZ_TRUE;
627                                 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, compLevel );
628                                 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
629                                         Error( "Failed creating zip archive \"%s\"!\n", packname );
630                                 }
631                                 mz_zip_reader_end( &zip);
632                                 mz_zip_writer_end( &zip );
633                         }
634                         else{
635                                 mz_zip_archive zip;
636                                 memset( &zip, 0, sizeof(zip) );
637                                 if( !mz_zip_writer_init_file( &zip, packname, 0 ) ){
638                                         Error( "Failed creating zip archive \"%s\"!\n", packname );
639                                 }
640                                 mz_bool success = MZ_TRUE;
641                                 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, compLevel );
642                                 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
643                                         Error( "Failed creating zip archive \"%s\"!\n", packname );
644                                 }
645                                 mz_zip_writer_end( &zip );
646                         }
647
648                         return qtrue;
649                 }
650         }
651
652         for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
653         {
654                 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
655
656                 if ( strcmp( file->name, fixed ) != 0 ) {
657                         continue;
658                 }
659
660                 memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
661
662                 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
663                         return qfalse;
664                 }
665
666                 bufferptr = safe_malloc( file->size + 1 );
667                 // we need to end the buffer with a 0
668                 ( (char*) ( bufferptr ) )[file->size] = 0;
669
670                 mz_uint16 DOS_time = (mz_uint16)(((file->zipinfo.cur_file_info.tmu_date.tm_hour) << 11) + ((file->zipinfo.cur_file_info.tmu_date.tm_min) << 5) + ((file->zipinfo.cur_file_info.tmu_date.tm_sec) >> 1));
671                 mz_uint16 DOS_date = (mz_uint16)(((file->zipinfo.cur_file_info.tmu_date.tm_year - 1980) << 9) + ((file->zipinfo.cur_file_info.tmu_date.tm_mon + 1) << 5) + file->zipinfo.cur_file_info.tmu_date.tm_mday);
672
673                 i = unzReadCurrentFile( file->zipfile, bufferptr, file->size );
674                 unzCloseCurrentFile( file->zipfile );
675                 if ( i < 0 ) {
676                         return qfalse;
677                 }
678                 else{
679                         mz_bool success = MZ_TRUE;
680                         success &= mz_zip_add_mem_to_archive_file_in_place_with_time( packname, filename, bufferptr, i, 0, 0, compLevel, DOS_time, DOS_date );
681                                 if ( !success ){
682                                         Error( "Failed creating zip archive \"%s\"!\n", packname );
683                                 }
684                         free( bufferptr );
685                         return qtrue;
686                 }
687         }
688
689         return qfalse;
690 #else
691                 Error( "Disabled because of miniz issue" );
692 #endif
693 }
694
695 qboolean vfsPackFile_Absolute_Path( const char *filepath, const char *filename, const char *packname, const int compLevel ){
696 #ifndef GARUX_DISABLE_BAD_MINIZ
697         char tmp[NAME_MAX];
698         strcpy( tmp, filepath );
699         if ( access( tmp, R_OK ) == 0 ) {
700                 if ( access( packname, R_OK ) == 0 ) {
701                         mz_zip_archive zip;
702                         memset( &zip, 0, sizeof(zip) );
703                         mz_zip_reader_init_file( &zip, packname, 0 );
704                         mz_zip_writer_init_from_reader( &zip, packname );
705
706                         mz_bool success = MZ_TRUE;
707                         success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, compLevel );
708                         if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
709                                 Error( "Failed creating zip archive \"%s\"!\n", packname );
710                         }
711                         mz_zip_reader_end( &zip);
712                         mz_zip_writer_end( &zip );
713                 }
714                 else{
715                         mz_zip_archive zip;
716                         memset( &zip, 0, sizeof(zip) );
717                         if( !mz_zip_writer_init_file( &zip, packname, 0 ) ){
718                                 Error( "Failed creating zip archive \"%s\"!\n", packname );
719                         }
720                         mz_bool success = MZ_TRUE;
721                         success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, compLevel );
722                         if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
723                                 Error( "Failed creating zip archive \"%s\"!\n", packname );
724                         }
725                         mz_zip_writer_end( &zip );
726                 }
727
728                 return qtrue;
729         }
730
731         return qfalse;
732 #else
733                 Error( "Disabled because of miniz issue" );
734 #endif
735 }