diff --git a/Makefile b/Makefile index b1b8efd8..7cbe3f43 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,9 @@ endif # work on all CPUs. Adjust to your needs # CFLAFS_BASE += -mmmx -msse -msse2 -msse3 -m3dnow +# OGG/Vorbis +OGGCFLAGS = -I/usr/include -I/usr/local/include + # SDL SDLCFLAGS = $(shell sdl-config --cflags) @@ -100,9 +103,15 @@ ifeq ($(OSTYPE),FreeBSD) LDFLAGS=-lm endif +# OGG/Vorbis +OGGLDFLAGS = -lvorbis -lvorbisfile -logg + # SDL SDLLDFLAGS=$(shell sdl-config --libs) +# ZLib +ZLDFLAGS = -lz + # OpenGL OPENGLLDFLAGS = -shared @@ -129,6 +138,7 @@ client: build/posix/vid \ build/sdl \ build/server \ + build/unzip \ release $(MAKE) release/quake2 @@ -188,7 +198,8 @@ CLIENT_OBJS = \ build/client/menu/qmenu.o \ build/client/sound/snd_dma.o \ build/client/sound/snd_mem.o \ - build/client/sound/snd_mix.o + build/client/sound/snd_mix.o \ + build/client/sound/snd_ogg.o # --------- @@ -206,6 +217,13 @@ COMMON_OBJS = \ # ---------- +# Unzip Object +UNZIP_OBJ = \ + build/unzip/ioapi.o \ + build/unzip/unzip.o + +# ---------- + # Game ABI objets GAME_ABI_OBJS = \ build/gameabi/m_flash.o \ @@ -452,6 +470,9 @@ build/client/sound/snd_mem.o : src/client/sound/snd_mem.c build/client/sound/snd_mix.o : src/client/sound/snd_mix.c $(CC) $(CFLAGS_CLIENT) -o $@ -c $< +build/client/sound/snd_ogg.o : src/client/sound/snd_ogg.c + $(CC) $(CFLAGS_CLIENT) $(OGGCFLAGS) -o $@ -c $< + # --------- # Common build @@ -484,6 +505,14 @@ build/common/pmove.o : src/common/pmove.c # ---------- +build/unzip/ioapi.o : src/unzip/ioapi.c + $(CC) $(CFLAGS_CLIENT) -o $@ -c $< + +build/unzip/unzip.o : src/unzip/unzip.c + $(CC) $(CFLAGS_CLIENT) -o $@ -c $< + +# ---------- + # Game ABI build build/gameabi/m_flash.o : src/game/baseq2/m_flash.c $(CC) $(CFLAGS_CLIENT) -o $@ -c $< @@ -903,16 +932,17 @@ build/ctf/q_shared.o: src/game/ctf/q_shared.c # The client release/quake2 : $(CLIENT_OBJS) $(COMMON_OBJS) $(GAME_ABI_OBJS) \ - $(SERVER_OBJS) $(POSIX_OBJS) $(SDL_OBJS) + $(UNZIP_OBJ) $(SERVER_OBJS) $(POSIX_OBJS) $(SDL_OBJS) $(CC) $(CFLAGS_CLIENT) -o $@ $(CLIENT_OBJS) $(COMMON_OBJS) $(GAME_ABI_OBJS) \ - $(SERVER_OBJS) $(POSIX_OBJS) $(SDL_OBJS) $(LDFLAGS) $(SDLLDFLAGS) + $(SERVER_OBJS) $(POSIX_OBJS) $(SDL_OBJS) $(UNZIP_OBJ) $(LDFLAGS) \ + $(SDLLDFLAGS) $(OGGLDFLAGS) $(ZLDFLAGS) # Dedicated Server release/q2ded : $(DEDICATED_SERVER_OBJS) $(DEDICATED_SERVER_COMMON_OBJS) \ - $(GAME_ABI_OBJS) $(DEDICATED_SERVER_POSIX_OBJS) + $(GAME_ABI_OBJS) $(DEDICATED_SERVER_POSIX_OBJS) $(UNZIP_OBJ) $(CC) $(CFLAGS_DEDICATED_SERVER) -o $@ $(DEDICATED_SERVER_OBJS) \ - $(DEDICATED_SERVER_COMMON_OBJS) $(GAME_ABI_OBJS) \ - $(DEDICATED_SERVER_POSIX_OBJS) $(LDFLAGS) + $(DEDICATED_SERVER_COMMON_OBJS) $(GAME_ABI_OBJS) $(UNZIP_OBJ)\ + $(DEDICATED_SERVER_POSIX_OBJS) $(LDFLAGS) $(ZLDFLAGS) # OpenGL refresher release/ref_gl.so : $(OPENGL_OBJS) $(OPENGL_POSIX_OBJS) $(OPENGL_GAME_OBJS) diff --git a/src/client/cl_cin.c b/src/client/cl_cin.c index d9830e0d..3e485266 100644 --- a/src/client/cl_cin.c +++ b/src/client/cl_cin.c @@ -1,32 +1,30 @@ /* -Copyright (C) 1997-2001 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ + * Copyright (C) 1997-2001 Id Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ #include "header/client.h" -typedef struct -{ - byte *data; +typedef struct { + byte *data; int count; } cblock_t; -typedef struct -{ +typedef struct { qboolean restart_sound; int s_rate; int s_width; @@ -34,11 +32,11 @@ typedef struct int width; int height; - byte *pic; - byte *pic_pending; + byte *pic; + byte *pic_pending; - // order 1 huffman stuff - int *hnodes1; // [256][256][2]; + /* order 1 huffman stuff */ + int *hnodes1;/* [256][256][2]; */ int numhnodes1[256]; int h_used[512]; @@ -48,182 +46,160 @@ typedef struct cinematics_t cin; /* -================================================================= - -PCX LOADING - -================================================================= -*/ + * ================================================================= + * + * PCX LOADING + * + * ================================================================= + */ /* -============== -SCR_LoadPCX -============== -*/ -void SCR_LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height) + * ============== + * SCR_LoadPCX + * ============== + */ +void +SCR_LoadPCX(char *filename, byte ** pic, byte ** palette, int *width, int *height) { - byte *raw; - pcx_t *pcx; - int x, y; + byte *raw; + pcx_t *pcx; + int x , y; int len; - int dataByte, runLength; - byte *out, *pix; + int dataByte , runLength; + byte *out, *pix; *pic = NULL; - // - // load the file - // - len = FS_LoadFile (filename, (void **)&raw); + /* load the file */ + len = FS_LoadFile(filename, (void **)&raw); if (!raw) - return; // Com_Printf ("Bad pcx file %s\n", filename); + return; /* Com_Printf ("Bad pcx file %s\n", filename); */ - // - // parse the PCX file - // - pcx = (pcx_t *)raw; + /* parse the PCX file */ + pcx = (pcx_t *) raw; raw = &pcx->data; if (pcx->manufacturer != 0x0a - || pcx->version != 5 - || pcx->encoding != 1 - || pcx->bits_per_pixel != 8 - || pcx->xmax >= 640 - || pcx->ymax >= 480) - { - Com_Printf ("Bad pcx file %s\n", filename); + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 + || pcx->xmax >= 640 + || pcx->ymax >= 480) { + Com_Printf("Bad pcx file %s\n", filename); return; } - - out = Z_Malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); + out = Z_Malloc((pcx->ymax + 1) * (pcx->xmax + 1)); *pic = out; pix = out; - if (palette) - { + if (palette) { *palette = Z_Malloc(768); - memcpy (*palette, (byte *)pcx + len - 768, 768); + memcpy(*palette, (byte *) pcx + len - 768, 768); } - if (width) - *width = pcx->xmax+1; + *width = pcx->xmax + 1; if (height) - *height = pcx->ymax+1; + *height = pcx->ymax + 1; - for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) - { - for (x=0 ; x<=pcx->xmax ; ) - { + for (y = 0; y <= pcx->ymax; y++, pix += pcx->xmax + 1) { + for (x = 0; x <= pcx->xmax;) { dataByte = *raw++; - if((dataByte & 0xC0) == 0xC0) - { + if ((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = *raw++; - } - else + } else runLength = 1; - while(runLength-- > 0) + while (runLength-- > 0) pix[x++] = dataByte; } } - if ( raw - (byte *)pcx > len) - { - Com_Printf ("PCX file %s was malformed", filename); - Z_Free (*pic); + if (raw - (byte *) pcx > len) { + Com_Printf("PCX file %s was malformed", filename); + Z_Free(*pic); *pic = NULL; } - - FS_FreeFile (pcx); + FS_FreeFile(pcx); } -//============================================================= +/* ============================================================= */ /* -================== -SCR_StopCinematic -================== -*/ -void SCR_StopCinematic (void) + * ================== SCR_StopCinematic ================== + */ +void +SCR_StopCinematic(void) { - cl.cinematictime = 0; // done - if (cin.pic) - { - Z_Free (cin.pic); + cl.cinematictime = 0; /* done */ + if (cin.pic) { + Z_Free(cin.pic); cin.pic = NULL; } - if (cin.pic_pending) - { - Z_Free (cin.pic_pending); + if (cin.pic_pending) { + Z_Free(cin.pic_pending); cin.pic_pending = NULL; } - if (cl.cinematicpalette_active) - { + if (cl.cinematicpalette_active) { re.CinematicSetPalette(NULL); cl.cinematicpalette_active = false; } - if (cl.cinematic_file) - { - fclose (cl.cinematic_file); - cl.cinematic_file = NULL; + if (cl.cinematic_file) { + FS_FCloseFile((size_t)cl.cinematic_file); + cl.cinematic_file = 0; } - if (cin.hnodes1) - { - Z_Free (cin.hnodes1); + if (cin.hnodes1) { + Z_Free(cin.hnodes1); cin.hnodes1 = NULL; } - - // switch back down to 11 khz sound if necessary - if (cin.restart_sound) - { + /* switch back down to 11 khz sound if necessary */ + if (cin.restart_sound) { cin.restart_sound = false; - CL_Snd_Restart_f (); + CL_Snd_Restart_f(); } - } /* -==================== -SCR_FinishCinematic - -Called when either the cinematic completes, or it is aborted -==================== -*/ -void SCR_FinishCinematic (void) + * ==================== + * SCR_FinishCinematic + * + * Called when either the cinematic completes, or it is aborted + * ==================== + */ +void +SCR_FinishCinematic(void) { - // tell the server to advance to the next map / cinematic - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, va("nextserver %i\n", cl.servercount)); + /* tell the server to advance to the next map / cinematic */ + MSG_WriteByte(&cls.netchan.message, clc_stringcmd); + SZ_Print(&cls.netchan.message, va("nextserver %i\n", cl.servercount)); } -//========================================================================== +/* ========================================================================== */ /* -================== -SmallestNode1 -================== -*/ -int SmallestNode1 (int numhnodes) + * ================== + * SmallestNode1 + * ================== + */ +int +SmallestNode1(int numhnodes) { int i; - int best, bestnode; + int best , bestnode; best = 99999999; bestnode = -1; - for (i=0 ; i>=1; - //----------- - if (nodenum < 256) - { - hnodes = hnodesbase + (nodenum<<9); + nodenum = hnodes[nodenum * 2 + (inbyte & 1)]; + inbyte >>= 1; + /*----------- */ + if (nodenum < 256) { + hnodes = hnodesbase + (nodenum << 9); *out_p++ = nodenum; if (!--count) break; nodenum = cin.numhnodes1[nodenum]; } - nodenum = hnodes[nodenum*2 + (inbyte&1)]; - inbyte >>=1; - //----------- - if (nodenum < 256) - { - hnodes = hnodesbase + (nodenum<<9); + nodenum = hnodes[nodenum * 2 + (inbyte & 1)]; + inbyte >>= 1; + /*----------- */ + if (nodenum < 256) { + hnodes = hnodesbase + (nodenum << 9); *out_p++ = nodenum; if (!--count) break; nodenum = cin.numhnodes1[nodenum]; } - nodenum = hnodes[nodenum*2 + (inbyte&1)]; - inbyte >>=1; - //----------- - if (nodenum < 256) - { - hnodes = hnodesbase + (nodenum<<9); + nodenum = hnodes[nodenum * 2 + (inbyte & 1)]; + inbyte >>= 1; + /*----------- */ + if (nodenum < 256) { + hnodes = hnodesbase + (nodenum << 9); *out_p++ = nodenum; if (!--count) break; nodenum = cin.numhnodes1[nodenum]; } - nodenum = hnodes[nodenum*2 + (inbyte&1)]; - inbyte >>=1; - //----------- - if (nodenum < 256) - { - hnodes = hnodesbase + (nodenum<<9); + nodenum = hnodes[nodenum * 2 + (inbyte & 1)]; + inbyte >>= 1; + /*----------- */ + if (nodenum < 256) { + hnodes = hnodesbase + (nodenum << 9); *out_p++ = nodenum; if (!--count) break; nodenum = cin.numhnodes1[nodenum]; } - nodenum = hnodes[nodenum*2 + (inbyte&1)]; - inbyte >>=1; - //----------- - if (nodenum < 256) - { - hnodes = hnodesbase + (nodenum<<9); + nodenum = hnodes[nodenum * 2 + (inbyte & 1)]; + inbyte >>= 1; + /*----------- */ + if (nodenum < 256) { + hnodes = hnodesbase + (nodenum << 9); *out_p++ = nodenum; if (!--count) break; nodenum = cin.numhnodes1[nodenum]; } - nodenum = hnodes[nodenum*2 + (inbyte&1)]; - inbyte >>=1; - //----------- - if (nodenum < 256) - { - hnodes = hnodesbase + (nodenum<<9); + nodenum = hnodes[nodenum * 2 + (inbyte & 1)]; + inbyte >>= 1; + /*----------- */ + if (nodenum < 256) { + hnodes = hnodesbase + (nodenum << 9); *out_p++ = nodenum; if (!--count) break; nodenum = cin.numhnodes1[nodenum]; } - nodenum = hnodes[nodenum*2 + (inbyte&1)]; - inbyte >>=1; - //----------- - if (nodenum < 256) - { - hnodes = hnodesbase + (nodenum<<9); + nodenum = hnodes[nodenum * 2 + (inbyte & 1)]; + inbyte >>= 1; + /*----------- */ + if (nodenum < 256) { + hnodes = hnodesbase + (nodenum << 9); *out_p++ = nodenum; if (!--count) break; nodenum = cin.numhnodes1[nodenum]; } - nodenum = hnodes[nodenum*2 + (inbyte&1)]; - inbyte >>=1; + nodenum = hnodes[nodenum * 2 + (inbyte & 1)]; + inbyte >>= 1; } - if (input - in.data != in.count && input - in.data != in.count+1) - { - Com_Printf ("Decompression overread by %i", (input - in.data) - in.count); + if (input - in.data != in.count && input - in.data != in.count + 1) { + Com_Printf("Decompression overread by %i", (input - in.data) - in.count); } out.count = out_p - out.data; @@ -420,58 +385,57 @@ cblock_t Huff1Decompress (cblock_t in) } /* -================== -SCR_ReadNextFrame -================== -*/ -byte *SCR_ReadNextFrame (void) + * ================== + * SCR_ReadNextFrame + * ================== + */ +byte * +SCR_ReadNextFrame(void) { int r; int command; - byte samples[22050/14*4]; - byte compressed[0x20000]; + byte samples [22050 / 14 * 4]; + byte compressed[0x20000]; int size; - byte *pic; - cblock_t in, huf1; - int start, end, count; + byte *pic; + cblock_t in , huf1; + int start , end, count; - // read the next frame - r = fread (&command, 4, 1, cl.cinematic_file); - if (r == 0) // we'll give it one more chance - r = fread (&command, 4, 1, cl.cinematic_file); + /* read the next frame */ + r = FS_FRead(&command, 4, 1, (size_t)cl.cinematic_file); + if (r == 0) /* we'll give it one more chance */ + r = FS_FRead(&command, 4, 1, (size_t)cl.cinematic_file); - if (r != 1) + if (r != 4) /* was 1 */ return NULL; command = LittleLong(command); if (command == 2) - return NULL; // last frame marker + return NULL; /* last frame marker */ - if (command == 1) - { // read palette - FS_Read (cl.cinematicpalette, sizeof(cl.cinematicpalette), cl.cinematic_file); - cl.cinematicpalette_active=0; // dubious.... exposes an edge case + if (command == 1) { /* read palette */ + FS_Read(cl.cinematicpalette, sizeof(cl.cinematicpalette), (size_t)cl.cinematic_file); + cl.cinematicpalette_active = 0; /* dubious.... exposes an edge case */ } - - // decompress the next frame - FS_Read (&size, 4, cl.cinematic_file); + /* decompress the next frame */ + FS_Read(&size, 4, (size_t)cl.cinematic_file); size = LittleLong(size); if (size > sizeof(compressed) || size < 1) - Com_Error (ERR_DROP, "Bad compressed frame size"); - FS_Read (compressed, size, cl.cinematic_file); + Com_Error(ERR_DROP, "Bad compressed frame size"); + FS_Read(compressed, size, (size_t)cl.cinematic_file); - // read sound - start = cl.cinematicframe*cin.s_rate/14; - end = (cl.cinematicframe+1)*cin.s_rate/14; + /* read sound */ + start = cl.cinematicframe * cin.s_rate / 14; + end = (cl.cinematicframe + 1) * cin.s_rate / 14; count = end - start; - FS_Read (samples, count*cin.s_width*cin.s_channels, cl.cinematic_file); + FS_Read(samples, count * cin.s_width * cin.s_channels, (size_t)cl.cinematic_file); - S_RawSamples (count, cin.s_rate, cin.s_width, cin.s_channels, samples); + S_RawSamples(count, cin.s_rate, cin.s_width, cin.s_channels, samples); in.data = compressed; in.count = size; - huf1 = Huff1Decompress (in); + huf1 = Huff1Decompress(in); pic = huf1.data; @@ -482,168 +446,151 @@ byte *SCR_ReadNextFrame (void) /* -================== -SCR_RunCinematic - -================== -*/ -void SCR_RunCinematic (void) + * ================== + * SCR_RunCinematic + * ================== + */ +void +SCR_RunCinematic(void) { int frame; - if (cl.cinematictime <= 0) - { - SCR_StopCinematic (); + if (cl.cinematictime <= 0) { + SCR_StopCinematic(); return; } - if (cl.cinematicframe == -1) - return; // static image + return; /* static image */ - if (cls.key_dest != key_game) - { // pause if menu or console is up - cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14; + if (cls.key_dest != key_game) + { /* pause if menu or console is up */ + cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14; return; } - - frame = (cls.realtime - cl.cinematictime)*14.0/1000; + frame = (cls.realtime - cl.cinematictime) * 14.0 / 1000; if (frame <= cl.cinematicframe) return; - if (frame > cl.cinematicframe+1) - { - Com_Printf ("Dropped frame: %i > %i\n", frame, cl.cinematicframe+1); - cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14; + if (frame > cl.cinematicframe + 1) { + Com_Printf("Dropped frame: %i > %i\n", frame, cl.cinematicframe + 1); + cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14; } if (cin.pic) - Z_Free (cin.pic); + Z_Free(cin.pic); cin.pic = cin.pic_pending; cin.pic_pending = NULL; - cin.pic_pending = SCR_ReadNextFrame (); - if (!cin.pic_pending) - { - SCR_StopCinematic (); - SCR_FinishCinematic (); - cl.cinematictime = 1; // hack to get the black screen behind loading - SCR_BeginLoadingPlaque (); + cin.pic_pending = SCR_ReadNextFrame(); + if (!cin.pic_pending) { + SCR_StopCinematic(); + SCR_FinishCinematic(); + cl.cinematictime = 1; /* hack to get the black screen behind loading */ + SCR_BeginLoadingPlaque(); cl.cinematictime = 0; return; } } /* -================== -SCR_DrawCinematic - -Returns true if a cinematic is active, meaning the view rendering -should be skipped -================== -*/ -qboolean SCR_DrawCinematic (void) + * ================== + * SCR_DrawCinematic + * + * Returns true if a cinematic is active, meaning the view rendering should be + * skipped + * ================== + */ +qboolean +SCR_DrawCinematic(void) { - if (cl.cinematictime <= 0) - { + if (cl.cinematictime <= 0) { return false; } - - if (cls.key_dest == key_menu) - { // blank screen and pause if menu is up + if (cls.key_dest == key_menu) { /* blank screen and pause if menu is up */ re.CinematicSetPalette(NULL); cl.cinematicpalette_active = false; return true; } - - if (!cl.cinematicpalette_active) - { + if (!cl.cinematicpalette_active) { re.CinematicSetPalette(cl.cinematicpalette); cl.cinematicpalette_active = true; } - if (!cin.pic) return true; - re.DrawStretchRaw (0, 0, viddef.width, viddef.height, - cin.width, cin.height, cin.pic); + re.DrawStretchRaw(0, 0, viddef.width, viddef.height, + cin.width, cin.height, cin.pic); return true; } /* -================== -SCR_PlayCinematic - -================== -*/ -void SCR_PlayCinematic (char *arg) + * ================== + * SCR_PlayCinematic + * ================== + */ +void +SCR_PlayCinematic(char *arg) { int width, height; - byte *palette; - char name[MAX_OSPATH], *dot; + byte *palette; + char name[MAX_OSPATH], *dot; int old_khz; - // make sure CD isn't playing music + /* make sure CD isn't playing music */ CDAudio_Stop(); + OGG_Stop(); cl.cinematicframe = 0; - dot = strstr (arg, "."); - if (dot && !strcmp (dot, ".pcx")) - { // static pcx image - Com_sprintf (name, sizeof(name), "pics/%s", arg); - SCR_LoadPCX (name, &cin.pic, &palette, &cin.width, &cin.height); + dot = strstr(arg, "."); + if (dot && !strcmp(dot, ".pcx")) { /* static pcx image */ + Com_sprintf(name, sizeof(name), "pics/%s", arg); + SCR_LoadPCX(name, &cin.pic, &palette, &cin.width, &cin.height); cl.cinematicframe = -1; cl.cinematictime = 1; - SCR_EndLoadingPlaque (); + SCR_EndLoadingPlaque(); cls.state = ca_active; - if (!cin.pic) - { - Com_Printf ("%s not found.\n", name); + if (!cin.pic) { + Com_Printf("%s not found.\n", name); cl.cinematictime = 0; - } - else - { - memcpy (cl.cinematicpalette, palette, sizeof(cl.cinematicpalette)); - Z_Free (palette); + } else { + memcpy(cl.cinematicpalette, palette, sizeof(cl.cinematicpalette)); + Z_Free(palette); } return; } - - Com_sprintf (name, sizeof(name), "video/%s", arg); - FS_FOpenFile (name, &cl.cinematic_file); - if (!cl.cinematic_file) - { - SCR_FinishCinematic (); - cl.cinematictime = 0; // done + Com_sprintf(name, sizeof(name), "video/%s", arg); + FS_FOpenFile(name, (fileHandle_t *) &cl.cinematic_file, FS_READ); + if (!cl.cinematic_file) { + /* Com_Error (ERR_DROP, "Cinematic %s not found.\n", name); */ + SCR_FinishCinematic(); + cl.cinematictime = 0; /* done */ return; } - - SCR_EndLoadingPlaque (); + SCR_EndLoadingPlaque(); cls.state = ca_active; - FS_Read (&width, 4, cl.cinematic_file); - FS_Read (&height, 4, cl.cinematic_file); + FS_Read(&width, 4, (size_t)cl.cinematic_file); + FS_Read(&height, 4, (size_t)cl.cinematic_file); cin.width = LittleLong(width); cin.height = LittleLong(height); - FS_Read (&cin.s_rate, 4, cl.cinematic_file); + FS_Read(&cin.s_rate, 4, (size_t)cl.cinematic_file); cin.s_rate = LittleLong(cin.s_rate); - FS_Read (&cin.s_width, 4, cl.cinematic_file); + FS_Read(&cin.s_width, 4, (size_t)cl.cinematic_file); cin.s_width = LittleLong(cin.s_width); - FS_Read (&cin.s_channels, 4, cl.cinematic_file); + FS_Read(&cin.s_channels, 4, (size_t)cl.cinematic_file); cin.s_channels = LittleLong(cin.s_channels); - Huff1TableInit (); + Huff1TableInit(); - // switch up to 22 khz sound if necessary - old_khz = Cvar_VariableValue ("s_khz"); - if (old_khz != cin.s_rate/1000) - { + /* switch up to 22 khz sound if necessary */ + old_khz = Cvar_VariableValue("s_khz"); + if (old_khz != cin.s_rate / 1000) { cin.restart_sound = true; - Cvar_SetValue ("s_khz", cin.s_rate/1000); - CL_Snd_Restart_f (); - Cvar_SetValue ("s_khz", old_khz); + Cvar_SetValue("s_khz", cin.s_rate / 1000); + CL_Snd_Restart_f(); + Cvar_SetValue("s_khz", old_khz); } - cl.cinematicframe = 0; - cin.pic = SCR_ReadNextFrame (); - cl.cinematictime = Sys_Milliseconds (); + cin.pic = SCR_ReadNextFrame(); + cl.cinematictime = Sys_Milliseconds(); } diff --git a/src/client/cl_main.c b/src/client/cl_main.c index 1933b93e..57c46e00 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -1798,7 +1798,6 @@ void CL_Init (void) CL_InitLocal (); IN_Init (); -// Cbuf_AddText ("exec autoexec.cfg\n"); FS_ExecAutoexec (); Cbuf_Execute (); @@ -1827,6 +1826,7 @@ void CL_Shutdown(void) CL_WriteConfiguration (); CDAudio_Shutdown (); + OGG_Stop(); S_Shutdown(); IN_Shutdown (); VID_Shutdown(); diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c index 972e71e3..6edf8474 100644 --- a/src/client/cl_parse.c +++ b/src/client/cl_parse.c @@ -532,7 +532,10 @@ void CL_ParseConfigString (void) else if (i == CS_CDTRACK) { if (cl.refresh_prepped) + { CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true); + OGG_ResumeCmd(); + } } else if (i >= CS_MODELS && i < CS_MODELS+MAX_MODELS) { diff --git a/src/client/cl_scrn.c b/src/client/cl_scrn.c index e5d3071d..d9af60e1 100644 --- a/src/client/cl_scrn.c +++ b/src/client/cl_scrn.c @@ -561,6 +561,7 @@ void SCR_BeginLoadingPlaque (void) S_StopAllSounds (); cl.sound_prepped = false; // don't play ambients CDAudio_Stop (); + OGG_Stop(); if (cls.disable_screen) return; if (developer->value) diff --git a/src/client/cl_view.c b/src/client/cl_view.c index be0945ce..ced2adf8 100644 --- a/src/client/cl_view.c +++ b/src/client/cl_view.c @@ -355,6 +355,17 @@ void CL_PrepRefresh (void) } else{ CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true); + + /* OGG/Vorbis */ + if (atoi(cl.configstrings[CS_CDTRACK]) < 10) + { + char tmp[2] = "0"; + OGG_ParseCmd(strcat(tmp, cl.configstrings[CS_CDTRACK])); + } + else + { + OGG_ParseCmd(cl.configstrings[CS_CDTRACK]); + } } } diff --git a/src/client/header/client.h b/src/client/header/client.h index e910539a..17446bdb 100644 --- a/src/client/header/client.h +++ b/src/client/header/client.h @@ -43,6 +43,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "../input/keys.h" #include "../console/console.h" #include "../sound/cdaudio.h" +#include "../sound/snd_ogg.h" //============================================================================= diff --git a/src/client/menu/menu.c b/src/client/menu/menu.c index c0fbec7f..03a935fd 100644 --- a/src/client/menu/menu.c +++ b/src/client/menu/menu.c @@ -30,23 +30,23 @@ static char *menu_move_sound = "misc/menu2.wav"; static char *menu_out_sound = "misc/menu3.wav"; void M_Menu_Main_f (void); - static void M_Menu_Game_f (void); - static void M_Menu_LoadGame_f (void); - static void M_Menu_SaveGame_f (void); - static void M_Menu_PlayerConfig_f (void); - static void M_Menu_DownloadOptions_f (void); - static void M_Menu_Credits_f( void ); - static void M_Menu_Multiplayer_f( void ); - static void M_Menu_JoinServer_f (void); - static void M_Menu_AddressBook_f( void ); - static void M_Menu_StartServer_f (void); - static void M_Menu_DMOptions_f (void); - static void M_Menu_Video_f (void); - static void M_Menu_Options_f (void); - static void M_Menu_Keys_f (void); - static void M_Menu_Quit_f (void); +static void M_Menu_Game_f (void); +static void M_Menu_LoadGame_f (void); +static void M_Menu_SaveGame_f (void); +static void M_Menu_PlayerConfig_f (void); +static void M_Menu_DownloadOptions_f (void); +static void M_Menu_Credits_f( void ); +static void M_Menu_Multiplayer_f( void ); +static void M_Menu_JoinServer_f (void); +static void M_Menu_AddressBook_f( void ); +static void M_Menu_StartServer_f (void); +static void M_Menu_DMOptions_f (void); +static void M_Menu_Video_f (void); +static void M_Menu_Options_f (void); +static void M_Menu_Keys_f (void); +static void M_Menu_Quit_f (void); - void M_Menu_Credits( void ); +void M_Menu_Credits( void ); qboolean m_entersound; // play after drawing a frame, so caching // won't disrupt the sound @@ -1010,7 +1010,8 @@ static menulist_s s_options_crosshair_box; static menuslider_s s_options_sfxvolume_slider; static menulist_s s_options_joystick_box; static menulist_s s_options_cdvolume_box; -static menulist_s s_options_cdshuffle_box; +static menulist_s s_options_cdshuffle_box; +static menulist_s s_options_oggvolume_box; static menulist_s s_options_quality_list; static menulist_s s_options_compatibility_list; static menulist_s s_options_console_action; @@ -1057,7 +1058,14 @@ static void ControlsSetMenuItemValues( void ) s_options_sfxvolume_slider.curvalue = Cvar_VariableValue( "s_volume" ) * 10; s_options_cdvolume_box.curvalue = !Cvar_VariableValue("cd_nocd"); - s_options_cdshuffle_box.curvalue = Cvar_VariableValue("cd_shuffle"); + s_options_oggvolume_box.curvalue = Cvar_VariableValue("ogg_enable"); + + cvar_t *ogg; + ogg = Cvar_Get("ogg_sequence", "1", CVAR_ARCHIVE); + if(!strcmp(ogg->string, "random")) + s_options_cdshuffle_box.curvalue = 1; + else + s_options_cdshuffle_box.curvalue = 0; s_options_quality_list.curvalue = !Cvar_VariableValue( "s_loadas8bit" ); s_options_sensitivity_slider.curvalue = ( sensitivity->value ) * 2; @@ -1113,14 +1121,46 @@ static void UpdateVolumeFunc( void *unused ) static void CDShuffleFunc(void *unused) { - Cvar_SetValue("cd_shuffle", s_options_cdshuffle_box.curvalue); + Cvar_SetValue("cd_shuffle", s_options_cdshuffle_box.curvalue); + + cvar_t *ogg; + ogg = Cvar_Get("ogg_enable", "1", CVAR_ARCHIVE); + + if (s_options_cdshuffle_box.curvalue) + { + Cvar_Set("ogg_sequence", "random"); + + if(ogg->value) + { + OGG_ParseCmd("?"); + OGG_Stop(); + } + } + else + { + Cvar_Set("ogg_sequence", "loop"); + if(ogg->value) + { + if (atoi(cl.configstrings[CS_CDTRACK]) < 10) + { + char tmp[2] = "0"; + OGG_ParseCmd(strcat(tmp, cl.configstrings[CS_CDTRACK])); + } + else + { + OGG_ParseCmd(cl.configstrings[CS_CDTRACK]); + } + } + } } static void UpdateCDVolumeFunc( void *unused ) { Cvar_SetValue( "cd_nocd", (float)!s_options_cdvolume_box.curvalue ); + Cvar_SetValue( "ogg_enable", 0 ); if (s_options_cdvolume_box.curvalue) { + OGG_Shutdown(); CDAudio_Init(); if (s_options_cdshuffle_box.curvalue) { @@ -1137,6 +1177,31 @@ static void UpdateCDVolumeFunc( void *unused ) } } +static void UpdateOGGVolumeFunc( void *unused ) +{ + Cvar_SetValue( "ogg_enable", (float)s_options_oggvolume_box.curvalue ); + Cvar_SetValue( "cd_nocd", 1 ); + if (s_options_oggvolume_box.curvalue) + { + CDAudio_Stop(); + OGG_Init(); + OGG_Stop(); + if (atoi(cl.configstrings[CS_CDTRACK]) < 10) + { + char tmp[2] = "0"; + OGG_ParseCmd(strcat(tmp, cl.configstrings[CS_CDTRACK])); + } + else + { + OGG_ParseCmd(cl.configstrings[CS_CDTRACK]); + } + } + else + { + OGG_Shutdown(); + } +} + extern void Key_ClearTyping( void ); static void ConsoleFunc( void *unused ) { @@ -1190,7 +1255,14 @@ static void Options_MenuInit( void ) "enabled", 0 }; - + + static const char *ogg_music_items[] = + { + "disabled", + "enabled", + 0 + }; + static const char *cd_shuffle[] = { "disabled", @@ -1248,17 +1320,25 @@ static void Options_MenuInit( void ) s_options_cdvolume_box.itemnames = cd_music_items; s_options_cdvolume_box.curvalue = !Cvar_VariableValue("cd_nocd"); + s_options_oggvolume_box.generic.type = MTYPE_SPINCONTROL; + s_options_oggvolume_box.generic.x = 0; + s_options_oggvolume_box.generic.y = 20; + s_options_oggvolume_box.generic.name = "OGG music"; + s_options_oggvolume_box.generic.callback = UpdateOGGVolumeFunc; + s_options_oggvolume_box.itemnames = ogg_music_items; + s_options_oggvolume_box.curvalue = Cvar_VariableValue("ogg_enable"); + s_options_cdshuffle_box.generic.type = MTYPE_SPINCONTROL; s_options_cdshuffle_box.generic.x = 0; - s_options_cdshuffle_box.generic.y = 20; - s_options_cdshuffle_box.generic.name = "CD shuffle"; + s_options_cdshuffle_box.generic.y = 30; + s_options_cdshuffle_box.generic.name = "Shuffle"; s_options_cdshuffle_box.generic.callback = CDShuffleFunc; s_options_cdshuffle_box.itemnames = cd_shuffle; s_options_cdshuffle_box.curvalue = Cvar_VariableValue("cd_shuffle");; - + s_options_quality_list.generic.type = MTYPE_SPINCONTROL; s_options_quality_list.generic.x = 0; - s_options_quality_list.generic.y = 30;; + s_options_quality_list.generic.y = 40;; s_options_quality_list.generic.name = "sound quality"; s_options_quality_list.generic.callback = UpdateSoundQualityFunc; s_options_quality_list.itemnames = quality_items; @@ -1266,7 +1346,7 @@ static void Options_MenuInit( void ) s_options_compatibility_list.generic.type = MTYPE_SPINCONTROL; s_options_compatibility_list.generic.x = 0; - s_options_compatibility_list.generic.y = 40; + s_options_compatibility_list.generic.y = 50; s_options_compatibility_list.generic.name = "sound compatibility"; s_options_compatibility_list.generic.callback = UpdateSoundQualityFunc; s_options_compatibility_list.itemnames = compatibility_items; @@ -1274,7 +1354,7 @@ static void Options_MenuInit( void ) s_options_sensitivity_slider.generic.type = MTYPE_SLIDER; s_options_sensitivity_slider.generic.x = 0; - s_options_sensitivity_slider.generic.y = 60; + s_options_sensitivity_slider.generic.y = 70; s_options_sensitivity_slider.generic.name = "mouse speed"; s_options_sensitivity_slider.generic.callback = MouseSpeedFunc; s_options_sensitivity_slider.minvalue = 2; @@ -1282,68 +1362,68 @@ static void Options_MenuInit( void ) s_options_alwaysrun_box.generic.type = MTYPE_SPINCONTROL; s_options_alwaysrun_box.generic.x = 0; - s_options_alwaysrun_box.generic.y = 70; + s_options_alwaysrun_box.generic.y = 80; s_options_alwaysrun_box.generic.name = "always run"; s_options_alwaysrun_box.generic.callback = AlwaysRunFunc; s_options_alwaysrun_box.itemnames = yesno_names; s_options_invertmouse_box.generic.type = MTYPE_SPINCONTROL; s_options_invertmouse_box.generic.x = 0; - s_options_invertmouse_box.generic.y = 80; + s_options_invertmouse_box.generic.y = 90; s_options_invertmouse_box.generic.name = "invert mouse"; s_options_invertmouse_box.generic.callback = InvertMouseFunc; s_options_invertmouse_box.itemnames = yesno_names; s_options_lookspring_box.generic.type = MTYPE_SPINCONTROL; s_options_lookspring_box.generic.x = 0; - s_options_lookspring_box.generic.y = 90; + s_options_lookspring_box.generic.y = 100; s_options_lookspring_box.generic.name = "lookspring"; s_options_lookspring_box.generic.callback = LookspringFunc; s_options_lookspring_box.itemnames = yesno_names; s_options_lookstrafe_box.generic.type = MTYPE_SPINCONTROL; s_options_lookstrafe_box.generic.x = 0; - s_options_lookstrafe_box.generic.y = 100; + s_options_lookstrafe_box.generic.y = 110; s_options_lookstrafe_box.generic.name = "lookstrafe"; s_options_lookstrafe_box.generic.callback = LookstrafeFunc; s_options_lookstrafe_box.itemnames = yesno_names; s_options_freelook_box.generic.type = MTYPE_SPINCONTROL; s_options_freelook_box.generic.x = 0; - s_options_freelook_box.generic.y = 110; + s_options_freelook_box.generic.y = 120; s_options_freelook_box.generic.name = "free look"; s_options_freelook_box.generic.callback = FreeLookFunc; s_options_freelook_box.itemnames = yesno_names; s_options_crosshair_box.generic.type = MTYPE_SPINCONTROL; s_options_crosshair_box.generic.x = 0; - s_options_crosshair_box.generic.y = 120; + s_options_crosshair_box.generic.y = 130; s_options_crosshair_box.generic.name = "crosshair"; s_options_crosshair_box.generic.callback = CrosshairFunc; s_options_crosshair_box.itemnames = crosshair_names; s_options_joystick_box.generic.type = MTYPE_SPINCONTROL; s_options_joystick_box.generic.x = 0; - s_options_joystick_box.generic.y = 130; + s_options_joystick_box.generic.y = 140; s_options_joystick_box.generic.name = "use joystick"; s_options_joystick_box.generic.callback = JoystickFunc; s_options_joystick_box.itemnames = yesno_names; s_options_customize_options_action.generic.type = MTYPE_ACTION; s_options_customize_options_action.generic.x = 0; - s_options_customize_options_action.generic.y = 150; + s_options_customize_options_action.generic.y = 160; s_options_customize_options_action.generic.name = "customize controls"; s_options_customize_options_action.generic.callback = CustomizeControlsFunc; s_options_defaults_action.generic.type = MTYPE_ACTION; s_options_defaults_action.generic.x = 0; - s_options_defaults_action.generic.y = 160; + s_options_defaults_action.generic.y = 170; s_options_defaults_action.generic.name = "reset defaults"; s_options_defaults_action.generic.callback = ControlsResetDefaultsFunc; s_options_console_action.generic.type = MTYPE_ACTION; s_options_console_action.generic.x = 0; - s_options_console_action.generic.y = 170; + s_options_console_action.generic.y = 180; s_options_console_action.generic.name = "go to console"; s_options_console_action.generic.callback = ConsoleFunc; @@ -1351,6 +1431,7 @@ static void Options_MenuInit( void ) Menu_AddItem( &s_options_menu, ( void * ) &s_options_sfxvolume_slider ); Menu_AddItem( &s_options_menu, ( void * ) &s_options_cdvolume_box ); + Menu_AddItem( &s_options_menu, ( void * ) &s_options_oggvolume_box ); Menu_AddItem( &s_options_menu, ( void * ) &s_options_cdshuffle_box ); Menu_AddItem( &s_options_menu, ( void * ) &s_options_quality_list ); Menu_AddItem( &s_options_menu, ( void * ) &s_options_compatibility_list ); @@ -2064,7 +2145,7 @@ static void Create_Savestrings (void) } else { - FS_Read (m_savestrings[i], sizeof(m_savestrings[i]), f); + FS_Read (m_savestrings[i], sizeof(m_savestrings[i]), (size_t)f); fclose (f); m_savevalid[i] = true; } diff --git a/src/client/sound/snd_dma.c b/src/client/sound/snd_dma.c index ba0c91a0..e8c02067 100644 --- a/src/client/sound/snd_dma.c +++ b/src/client/sound/snd_dma.c @@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "../header/client.h" #include "snd_loc.h" +#include "snd_ogg.h" void S_Play(void); void S_SoundList(void); @@ -137,6 +138,8 @@ void S_Init (void) Cmd_AddCommand("stopsound", S_StopAllSounds); Cmd_AddCommand("soundlist", S_SoundList); Cmd_AddCommand("soundinfo", S_SoundInfo_f); + Cmd_AddCommand("ogg_init", OGG_Init); + Cmd_AddCommand("ogg_shutdown", OGG_Shutdown); if (!SNDDMA_Init()) return; @@ -152,6 +155,8 @@ void S_Init (void) Com_Printf ("sound sampling rate: %i\n", dma.speed); S_StopAllSounds (); + + OGG_Init(); } Com_Printf("------------------------------------\n"); @@ -170,6 +175,11 @@ void S_Shutdown(void) if (!sound_started) return; + OGG_Shutdown(); + + Cmd_RemoveCommand("ogg_init"); + Cmd_RemoveCommand("ogg_shutdown"); + SNDDMA_Shutdown(); sound_started = 0; @@ -621,11 +631,11 @@ struct sfx_s *S_RegisterSexedSound (entity_state_t *ent, char *base) if (!sfx) { // no, so see if it exists - FS_FOpenFile (&sexedFilename[1], &f); + FS_FOpenFile (&sexedFilename[1], (fileHandle_t *)&f, FS_READ); if (f) { // yes, close the file and register it - FS_FCloseFile (f); + FS_FCloseFile ((size_t)f); sfx = S_RegisterSound (sexedFilename); } else @@ -1111,6 +1121,9 @@ void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) // mix some sound S_Update_(); + + /* stream music */ + OGG_Stream(); } void GetSoundtime(void) diff --git a/src/common/cmodel.c b/src/common/cmodel.c index c4e51f73..efdbd199 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -1708,7 +1708,7 @@ and recalculates the area connections */ void CM_ReadPortalState (FILE *f) { - FS_Read (portalopen, sizeof(portalopen), f); + FS_Read (portalopen, sizeof(portalopen), (size_t)f); FloodAreaConnections (); } diff --git a/src/common/files.c b/src/common/files.c index ba36f419..a0d00817 100644 --- a/src/common/files.c +++ b/src/common/files.c @@ -1,850 +1,1836 @@ /* -Copyright (C) 1997-2001 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ + * Copyright (C) 1997-2001 Id Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ #include "qcommon.h" - - -// if a packfile directory differs from this, it is assumed to be hacked -// Full version -#define PAK0_CHECKSUM 0x40e614e0 +#include "../posix/glob/glob.h" +#include "../unzip/unzip.h" /* -============================================================================= + * ============================================================================ + * + * QUAKE FILESYSTEM + * + * ============================================================================= + */ -QUAKE FILESYSTEM +#define MAX_HANDLES 32 +#define MAX_READ 0x10000 +#define MAX_WRITE 0x10000 +#define MAX_FIND_FILES 0x04000 +#define MAX_PAKS 100 -============================================================================= -*/ +/* Berserk's pk3 file support. */ +typedef struct { + char name[MAX_QPATH]; + fsMode_t mode; + FILE *file; /* Only one will be used. */ + unzFile *zip; /* (file or zip) */ +} fsHandle_t; -// -// in memory -// +typedef struct fsLink_s { + char *from; + char *to; + int length; + struct fsLink_s *next; +} fsLink_t; -typedef struct -{ - char name[MAX_QPATH]; - int filepos, filelen; -} packfile_t; +typedef struct { + char name[MAX_QPATH]; + int size; + int offset; /* Ignored in PK3 files. */ +} fsPackFile_t; -typedef struct pack_s -{ - char filename[MAX_OSPATH]; - FILE *handle; - int numfiles; - packfile_t *files; -} pack_t; +typedef struct { + char name[MAX_OSPATH]; + int numFiles; + FILE *pak; + unzFile *pk3; + fsPackFile_t *files; +} fsPack_t; -char fs_gamedir[MAX_OSPATH]; -cvar_t *fs_basedir; -cvar_t *fs_cddir; -cvar_t *fs_gamedirvar; +typedef struct fsSearchPath_s { + char path[MAX_OSPATH]; /* Only one used. */ + fsPack_t *pack; /* (path or pack) */ + struct fsSearchPath_s *next; +} fsSearchPath_t; -typedef struct filelink_s -{ - struct filelink_s *next; - char *from; - int fromlength; - char *to; -} filelink_t; +typedef enum { + PAK, + PK3 +} fsPackFormat_t; -filelink_t *fs_links; +typedef struct { + char *suffix; + fsPackFormat_t format; +} fsPackTypes_t; -typedef struct searchpath_s -{ - char filename[MAX_OSPATH]; - pack_t *pack; // only one of filename / pack will be used - struct searchpath_s *next; -} searchpath_t; +fsHandle_t fs_handles[MAX_HANDLES]; +fsLink_t *fs_links; +fsSearchPath_t *fs_searchPaths; +fsSearchPath_t *fs_baseSearchPaths; -searchpath_t *fs_searchpaths; -searchpath_t *fs_base_searchpaths; // without gamedirs +fsPackTypes_t fs_packtypes[] = { /* Pack formats / suffixes. */ + {"pak", PAK}, + {"pk2", PK3}, + {"pk3", PK3}, + {"zip", PK3} +}; +char fs_gamedir[MAX_OSPATH]; +static char fs_currentGame[MAX_QPATH]; + +static char fs_fileInPath[MAX_OSPATH]; +static qboolean fs_fileInPack; + +/* Set by FS_FOpenFile. */ +int file_from_pak = 0; +int file_from_pk3 = 0; +char file_from_pk3_name[MAX_QPATH]; + +cvar_t *fs_homepath; +cvar_t *fs_basedir; +cvar_t *fs_cddir; +cvar_t *fs_gamedirvar; +cvar_t *fs_debug; + +fsHandle_t *FS_GetFileByHandle(fileHandle_t f); +char *Sys_GetCurrentDirectory(void); /* - -All of Quake's data access is through a hierchal file system, but the -contents of the file system can be transparently merged from several -sources. - -The "base directory" is the path to the directory holding the quake.exe -and all game directories. The sys_* files pass this to host_init in -quakeparms_t->basedir. This can be overridden with the "-basedir" -command line parm to allow code debugging in a different directory. The -base directory is only used during filesystem initialization. - -The "game directory" is the first tree on the search path and directory -that all generated files (savegames, screenshots, demos, config files) -will be saved to. This can be overridden with the "-game" command line -parameter. The game directory can never be changed while quake is -executing. This is a precacution against having a malicious server -instruct clients to write files over areas they shouldn't. - -*/ - + * All of Quake's data access is through a hierchal file system, but the + * contents of the file system can be transparently merged from several + * sources. + * + * The "base directory" is the path to the directory holding the quake.exe and + * all game directories. The sys_* files pass this to host_init in + * quakeparms_t->basedir. This can be overridden with the "-basedir" command + * line parm to allow code debugging in a different directory. The base + * directory is only used during filesystem initialization. + * + * The "game directory" is the first tree on the search path and directory that + * all generated files (savegames, screenshots, demos, config files) will be + * saved to. This can be overridden with the "-game" command line parameter. + * The game directory can never be changed while quake is executing. This is + * a precacution against having a malicious server instruct clients to write + * files over areas they shouldn't. + * + */ /* -================ -FS_filelength -================ -*/ -int FS_filelength (FILE *f) + * ================= + * Com_FilePath + * + * Returns the path up to, but not including the last '/'. + * ================= + */ +void +Com_FilePath(const char *path, char *dst, int dstSize) { - int pos; - int end; + char *pos; /* Position of the last '/'. */ - pos = ftell (f); - fseek (f, 0, SEEK_END); - end = ftell (f); - fseek (f, pos, SEEK_SET); - - return end; + if ((pos = strrchr(path, '/')) != NULL) { + pos--; + if ((pos - path) < dstSize) { + memcpy(dst, path, pos - path); + dst[pos - path] = '\0'; + } else { + Com_Printf("Com_FilePath: not enough space.\n"); + return; + } + } else + strncpy(dst, path, dstSize); } +/* + * ================= + * FS_FileLength + * ================= + */ +int +FS_FileLength(FILE * f) +{ + int pos; /* Current position. */ + int end; /* End of file. */ + + pos = ftell(f); + fseek(f, 0, SEEK_END); + end = ftell(f); + fseek(f, pos, SEEK_SET); + + return (end); +} /* -============ -FS_CreatePath - -Creates any directories needed to store the given filename -============ -*/ -void FS_CreatePath (char *path) + * ============ + * FS_CreatePath + * + * Creates any directories needed to store the given filename. + * ============ + */ +void +FS_CreatePath(char *path) { - char *ofs; - - for (ofs = path+1 ; *ofs ; ofs++) - { - if (*ofs == '/') - { // create the directory - *ofs = 0; - Sys_Mkdir (path); - *ofs = '/'; + char *cur; /* Current '/'. */ + char *old; /* Old '/'. */ + + FS_DPrintf("FS_CreatePath(%s)\n", path); + + if (strstr(path, "..") != NULL) { + Com_Printf("WARNING: refusing to create relative path '%s'.\n", path); + return; + } + cur = old = path; + while (cur != NULL) { + if ((cur - old) > 1) { + *cur = '\0'; + Sys_Mkdir(path); + *cur = '/'; } + old = cur; + cur = strchr(old + 1, '/'); } } - /* -============== -FS_FCloseFile - -For some reason, other dll's can't just cal fclose() -on files returned by FS_FOpenFile... -============== -*/ -void FS_FCloseFile (FILE *f) + * ================= + * FS_DPrintf + * ================= + */ +void +FS_DPrintf(const char *format,...) { - fclose (f); + char msg[1024]; + va_list argPtr; + + if (fs_debug->value != 1) + return; + + va_start(argPtr, format); + vsnprintf(msg, sizeof(msg), format, argPtr); + va_end(argPtr); + + Com_Printf("%s", msg); } - -// RAFAEL -/* - Developer_searchpath -*/ -int Developer_searchpath (int who) +char * +FS_Gamedir(void) +{ + return (fs_gamedir); +} + +/* + * ================= + * FS_DeletePath + * + * TODO: delete tree contents. + * ================= + */ +void +FS_DeletePath(char *path) +{ + FS_DPrintf("FS_DeletePath(%s)\n", path); + + Sys_Rmdir(path); +} + +/* + * ================= + * FS_FileForHandle + * + * Returns a FILE * for a fileHandle_t. + * ================= + */ +FILE * +FS_FileForHandle(fileHandle_t f) +{ + fsHandle_t *handle; + + handle = FS_GetFileByHandle(f); + + if (handle->zip != NULL) + Com_Error(ERR_DROP, "FS_FileForHandle: can't get FILE on zip file"); + + if (handle->file == NULL) + Com_Error(ERR_DROP, "FS_FileForHandle: NULL"); + + return (handle->file); +} + +/* + * ================= + * FS_HandleForFile + * + * Finds a free fileHandle_t. + * ================= + */ +fsHandle_t * +FS_HandleForFile(const char *path, fileHandle_t * f) +{ + int i; + fsHandle_t *handle; + + handle = fs_handles; + for (i = 0; i < MAX_HANDLES; i++, handle++) { + if (handle->file == NULL && handle->zip == NULL) { + strncpy(handle->name, path, sizeof(handle->name)); + *f = i + 1; + return (handle); + } + } + + /* Failed. */ + Com_Error(ERR_DROP, "FS_HandleForFile: none free"); + + return (NULL); +} + +/* + * ================= + * FS_GetFileByHandle + * + * Returns a fsHandle_t * for the given fileHandle_t. + * ================= + */ +fsHandle_t * +FS_GetFileByHandle(fileHandle_t f) +{ + if (f <= 0 || f > MAX_HANDLES) + Com_Error(ERR_DROP, "FS_GetFileByHandle: out of range"); + + return (&fs_handles[f - 1]); +} + +/* + * ================= + * FS_FOpenFileAppend + * + * Returns file size or -1 on error. + * ================= + */ +int +FS_FOpenFileAppend(fsHandle_t * handle) +{ + char path[MAX_OSPATH]; + + FS_CreatePath(handle->name); + + Com_sprintf(path, sizeof(path), "%s/%s", fs_gamedir, handle->name); + + handle->file = fopen(path, "a"); + if (handle->file) { + if (fs_debug->value) + Com_Printf("FS_FOpenFileAppend: '%s'.\n", path); + return (FS_FileLength(handle->file)); + } + if (fs_debug->value) + Com_Printf("FS_FOpenFileAppend: couldn't open '%s'.\n", path); + + return (-1); +} + +/* + * ================= + * FS_FOpenFileWrite + * + * Always returns 0 or -1 on error. + * ================= + */ +int +FS_FOpenFileWrite(fsHandle_t * handle) +{ + char path[MAX_OSPATH]; + + FS_CreatePath(handle->name); + + Com_sprintf(path, sizeof(path), "%s/%s", fs_gamedir, handle->name); + + if ((handle->file = fopen(path, "w")) == NULL) { + if (fs_debug->value) + Com_Printf("FS_FOpenFileWrite: '%s'.\n", path); + return (0); + } + if (fs_debug->value) + Com_Printf("FS_FOpenFileWrite: couldn't open '%s'.\n", path); + + return (-1); +} + +/* + * ================= + * FS_FOpenFileRead + * + * Returns file size or -1 if not found. Can open separate files as well as + * files inside pack files (both PAK and PK3). + * ================= + */ +int +FS_FOpenFileRead(fsHandle_t * handle) +{ + char path[MAX_OSPATH]; + int i; + fsSearchPath_t *search; + fsPack_t *pack; + + /* Knightmare - hack global vars for autodownloads. */ + file_from_pak = 0; + file_from_pk3 = 0; + + /* Search through the path, one element at a time. */ + for (search = fs_searchPaths; search; search = search->next) { + /* Search inside a pack file. */ + if (search->pack) { + pack = search->pack; + for (i = 0; i < pack->numFiles; i++) { + if (Q_stricmp(pack->files[i].name, handle->name) == 0) { + /* Found it! */ + Com_FilePath(pack->name, fs_fileInPath, sizeof(fs_fileInPath)); + fs_fileInPack = true; + + if (fs_debug->value) + Com_Printf("FS_FOpenFileRead: '%s' (found in '%s').\n", + handle->name, pack->name); + + if (pack->pak) { + /* PAK */ + file_from_pak = 1; + handle->file = fopen(pack->name, "r"); + + if (handle->file) { + fseek(handle->file, pack->files[i].offset, SEEK_SET); + return (pack->files[i].size); + } + } else if (pack->pk3) { + /* PK3 */ + file_from_pk3 = 1; + strncpy(file_from_pk3_name, strrchr(pack->name, '/') + 1, sizeof(file_from_pk3_name)); + handle->zip = unzOpen(pack->name); + if (handle->zip) { + if (unzLocateFile(handle->zip, handle->name, 2) == UNZ_OK) { + if (unzOpenCurrentFile(handle->zip) == UNZ_OK) + return (pack->files[i].size); + } + unzClose(handle->zip); + } + } + Com_Error(ERR_FATAL, "Couldn't reopen '%s'", pack->name); + } + } + } else { + /* Search in a directory tree. */ + Com_sprintf(path, sizeof(path), "%s/%s", search->path, handle->name); + + handle->file = fopen(path, "r"); + + if (!handle->file) + { + strlwr(path); + handle->file = fopen(path, "r"); + } + + if (!handle->file) + continue; + + if (handle->file) { + /* Found it! */ + strncpy(fs_fileInPath, search->path, sizeof(fs_fileInPath)); + fs_fileInPack = false; + + if (fs_debug->value) + Com_Printf("FS_FOpenFileRead: '%s' (found in '%s').\n", + handle->name, search->path); + + return FS_FileLength(handle->file); + } + } + } + + /* Not found! */ + fs_fileInPath[0] = 0; + fs_fileInPack = false; + + if (fs_debug->value) + Com_Printf("FS_FOpenFileRead: couldn't find '%s'.\n", handle->name); + + return (-1); +} + +/* + * ============== + * FS_FCloseFile + * + * Other dll's can't just call fclose() on files returned by FS_FOpenFile. + * ============== + */ +void +FS_FCloseFile(fileHandle_t f) +{ + fsHandle_t *handle; + + handle = FS_GetFileByHandle(f); + + if (handle->file) + fclose(handle->file); + else if (handle->zip) { + unzCloseCurrentFile(handle->zip); + unzClose(handle->zip); + } + memset(handle, 0, sizeof(*handle)); +} + +int +Developer_searchpath(int who) { - int ch; - // PMM - warning removal - searchpath_t *search; - - if (who == 1) // xatrix + + /* PMM - warning removal. */ + fsSearchPath_t *search; + + if (who == 1) /* xatrix */ ch = 'x'; else if (who == 2) ch = 'r'; - for (search = fs_searchpaths ; search ; search = search->next) - { - if (strstr (search->filename, "xatrix")) - return 1; + for (search = fs_searchPaths; search; search = search->next) { + if (strstr(search->path, "xatrix")) + return (1); + if (strstr(search->path, "rogue")) + return (2); - if (strstr (search->filename, "rogue")) - return 2; + /* + * if (strstr (search->path, "dday")) return (3); + */ } + return (0); - } - -/* -=========== -FS_FOpenFile - -Finds the file in the search path. -returns filesize and an open FILE * -Used for streaming data out of either a pak file or -a seperate file. -=========== -*/ -int file_from_pak = 0; -int FS_FOpenFile (char *filename, FILE **file) +qboolean +modType(char *name) { - searchpath_t *search; - char netpath[MAX_OSPATH]; - pack_t *pak; - int i; - filelink_t *link; + /* PMM - warning removal. */ + fsSearchPath_t *search; - file_from_pak = 0; - - // check for links first - for (link = fs_links ; link ; link=link->next) - { - if (!strncmp (filename, link->from, link->fromlength)) - { - Com_sprintf (netpath, sizeof(netpath), "%s%s",link->to, filename+link->fromlength); - *file = fopen (netpath, "rb"); - if (*file) - { - Com_DPrintf ("link file: %s\n",netpath); - return FS_filelength (*file); - } - return -1; - } + for (search = fs_searchPaths; search; search = search->next) { + if (strstr(search->path, name)) + return true; } - // - // search through the path, one element at a time - // - for (search = fs_searchpaths ; search ; search = search->next) - { - // is the element a pak file? - if (search->pack) - { - // look through all the pak file elements - pak = search->pack; - for (i=0 ; inumfiles ; i++) - if (!Q_strcasecmp (pak->files[i].name, filename)) - { // found it! - file_from_pak = 1; - Com_DPrintf ("PackFile: %s : %s\n",pak->filename, filename); - // open a new file on the pakfile - *file = fopen (pak->filename, "rb"); - if (!*file) - Com_Error (ERR_FATAL, "Couldn't reopen %s", pak->filename); - fseek (*file, pak->files[i].filepos, SEEK_SET); - return pak->files[i].filelen; - } - } + return (0); +} + +/* + * =========== + * FS_FOpenFile + * + * Finds the file in the search path. Returns filesize and an open FILE *. Used + * for streaming data out of either a pak file or a seperate file. + * =========== + */ +int +FS_FOpenFile(const char *name, fileHandle_t * f, fsMode_t mode) +{ + int size = 0; + fsHandle_t *handle; + + handle = FS_HandleForFile(name, f); + + strncpy(handle->name, name, sizeof(handle->name)); + handle->mode = mode; + + switch (mode) { + case FS_READ: + size = FS_FOpenFileRead(handle); + break; + case FS_WRITE: + size = FS_FOpenFileWrite(handle); + break; + case FS_APPEND: + size = FS_FOpenFileAppend(handle); + break; + default: + Com_Error(ERR_FATAL, "FS_FOpenFile: bad mode (%i)", mode); + break; + } + + if (size != -1) + return (size); + + /* Couldn't open, so free the handle. */ + memset(handle, 0, sizeof(*handle)); + *f = 0; + + return (-1); +} + +/* + * ================= + * FS_ReadFile + * + * Properly handles partial reads. + * ================= + */ +int +FS_Read(void *buffer, int size, fileHandle_t f) +{ + qboolean tried = false; /* Tried to read from a CD. */ + byte *buf; /* Buffer. */ + int r; /* Numbre of bytes read. */ + int remaining; /* Remaining bytes. */ + fsHandle_t *handle; /* File handle. */ + + handle = FS_GetFileByHandle(f); + + /* Read. */ + remaining = size; + buf = (byte *) buffer; + + while (remaining) { + if (handle->file) + r = fread(buf, 1, remaining, handle->file); + else if (handle->zip) + r = unzReadCurrentFile(handle->zip, buf, remaining); else - { - // check a file in the directory tree - - Com_sprintf (netpath, sizeof(netpath), "%s/%s",search->filename, filename); - - *file = fopen (netpath, "rb"); - if (!*file) - continue; - - Com_DPrintf ("FindFile: %s\n",netpath); + return (0); - return FS_filelength (*file); - } - - } - - Com_DPrintf ("FindFile: can't find %s\n", filename); - - *file = NULL; - return -1; -} - -/* -================= -FS_ReadFile - -Properly handles partial reads -================= -*/ -void CDAudio_Stop(void); -#define MAX_READ 0x10000 // read in blocks of 64k -void FS_Read (void *buffer, int len, FILE *f) -{ - int block, remaining; - int read; - byte *buf; - int tries; - - buf = (byte *)buffer; - - // read in chunks for progress bar - remaining = len; - tries = 0; - while (remaining) - { - block = remaining; - if (block > MAX_READ) - block = MAX_READ; - read = fread (buf, 1, block, f); - if (read == 0) - { - // we might have been trying to read from a CD - if (!tries) - { - tries = 1; -#ifndef DEDICATED_ONLY - CDAudio_Stop(); -#endif + if (r == 0) { + if (!tried) { + /* Might tried to read from a CD. */ + tried = true; + } else { + /* Already tried once. */ + Com_Error(ERR_FATAL, va("FS_Read: 0 bytes read from '%s'", handle->name)); + return (size - remaining); } + } else if (r == -1) + Com_Error(ERR_FATAL, "FS_Read: -1 bytes read from '%s'", handle->name); + + remaining -= r; + buf += r; + } + + return (size); +} + +/* + * ================= + * FS_FRead + * + * Properly handles partial reads of size up to count times. No error if it + * can't read. + * ================= + */ +int +FS_FRead(void *buffer, int size, int count, fileHandle_t f) +{ + qboolean tried = false; /* Tried to read from a CD. */ + byte *buf; /* Buffer. */ + int loops; /* Loop indicator. */ + int r; /* Number of bytes read. */ + int remaining; /* Remaining bytes. */ + fsHandle_t *handle; /* File handle. */ + + handle = FS_GetFileByHandle(f); + + /* Read. */ + loops = count; + buf = (byte *) buffer; + + while (loops) { + /* Read in chunks. */ + remaining = size; + while (remaining) { + if (handle->file) + r = fread(buf, 1, remaining, handle->file); + else if (handle->zip) + r = unzReadCurrentFile(handle->zip, buf, remaining); else - Com_Error (ERR_FATAL, "FS_Read: 0 bytes read"); + return (0); + + if (r == 0) { + if (!tried) { + /* Might tried to read from a CD. */ + tried = true; + } else { + /* Already tried once. */ + Com_Printf("FS_FRead: 0 bytes read from '%s'.\n", handle->name); + return (size - remaining); + } + } else if (r == -1) + Com_Error(ERR_FATAL, "FS_FRead: -1 bytes read from '%s'", handle->name); + + remaining -= r; + buf += r; } - - if (read == -1) - Com_Error (ERR_FATAL, "FS_Read: -1 bytes read"); - - // do some progress bar thing here... - - remaining -= read; - buf += read; - } -} - -/* -============ -FS_LoadFile - -Filename are reletive to the quake search path -a null buffer will just return the file length without loading -============ -*/ -int FS_LoadFile (char *path, void **buffer) -{ - FILE *h; - byte *buf; - int len; - - buf = NULL; // quiet compiler warning - - // look for it in the filesystem or pack files - len = FS_FOpenFile (path, &h); - if (!h) - { - if (buffer) - *buffer = NULL; - return -1; - } - - if (!buffer) - { - fclose (h); - return len; + loops--; } - buf = Z_Malloc(len); - *buffer = buf; - - FS_Read (buf, len, h); - - fclose (h); - - return len; -} - - -/* -============= -FS_FreeFile -============= -*/ -void FS_FreeFile (void *buffer) -{ - Z_Free (buffer); + return (size); } /* -================= -FS_LoadPackFile - -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. -================= -*/ -pack_t *FS_LoadPackFile (char *packfile) + * ================= + * FS_Write + * + * Properly handles partial writes. + * ================= + */ +int +FS_Write(const void *buffer, int size, fileHandle_t f) { - dpackheader_t header; - int i; - packfile_t *newfiles; - int numpackfiles; - pack_t *pack; - FILE *packhandle; - dpackfile_t info[MAX_FILES_IN_PACK]; - unsigned checksum; - packhandle = fopen(packfile, "rb"); - if (!packhandle) - return NULL; + byte *buf; /* Buffer. */ + int remaining; /* Remaining bytes. */ + int w = 0; /* Numbre of bytes written. */ + fsHandle_t *handle; /* File handle. */ - fread (&header, 1, sizeof(header), packhandle); - if (LittleLong(header.ident) != IDPAKHEADER) - Com_Error (ERR_FATAL, "%s is not a packfile", packfile); - header.dirofs = LittleLong (header.dirofs); - header.dirlen = LittleLong (header.dirlen); + handle = FS_GetFileByHandle(f); - numpackfiles = header.dirlen / sizeof(dpackfile_t); + /* Write. */ + remaining = size; + buf = (byte *) buffer; - if (numpackfiles > MAX_FILES_IN_PACK) - Com_Error (ERR_FATAL, "%s has %i files", packfile, numpackfiles); + while (remaining) { + if (handle->file) + w = fwrite(buf, 1, remaining, handle->file); + else if (handle->zip) + Com_Error(ERR_FATAL, "FS_Write: can't write to zip file '%s'", handle->name); + else + return (0); - newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t)); + if (w == 0) { + Com_Printf("FS_Write: 0 bytes written to '%s'.\n", handle->name); + return size - remaining; + } else if (w == -1) + Com_Error(ERR_FATAL, "FS_Write: -1 bytes written to '%s'", handle->name); - fseek (packhandle, header.dirofs, SEEK_SET); - fread (info, 1, header.dirlen, packhandle); - - // crc the directory to check for modifications - checksum = Com_BlockChecksum ((void *)info, header.dirlen); - - // parse the directory - for (i=0 ; ifilename, packfile); - pack->handle = packhandle; - pack->numfiles = numpackfiles; - pack->files = newfiles; - - Com_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); - return pack; -} - - -/* -================ -FS_AddGameDirectory - -Adds the directory to the head of the path, -then loads and adds pak1.pak pak2.pak ... -================ -*/ -void FS_AddGameDirectory (char *dir) -{ - int i; - searchpath_t *search; - pack_t *pak; - char pakfile[MAX_OSPATH]; - - // - // add the base directory to the search path - // - search = Z_Malloc (sizeof(searchpath_t)); - strncpy (search->filename, dir, sizeof(search->filename)-1); - search->filename[sizeof(search->filename)-1] = 0; - - search->next = fs_searchpaths; - fs_searchpaths = search; - - // - // add any pak files in the format pak0.pak pak1.pak, ... - // - for (i=0; i<50; i++) - { - Com_sprintf (pakfile, sizeof(pakfile), "%s/pak%i.pak", dir, i); - pak = FS_LoadPackFile (pakfile); - if (!pak) - continue; - search = Z_Malloc (sizeof(searchpath_t)); - search->pack = pak; - search->next = fs_searchpaths; - fs_searchpaths = search; - } + return (size); } /* -================ -FS_AddHomeAsGameDirectory - -Use ~/.quake2/dir as fs_gamedir -================ -*/ -void FS_AddHomeAsGameDirectory (char *dir) + * ================= + * FS_FTell + * ================= + */ +int +FS_FTell(fileHandle_t f) { - char gdir[MAX_OSPATH]; - char *homedir=getenv("HOME"); - if(homedir) - { - int len = snprintf(gdir,sizeof(gdir),"%s/.quake2/%s/", homedir, dir); - Com_Printf("using %s for writing\n",gdir); - FS_CreatePath (gdir); + fsHandle_t *handle; - if ((len > 0) && (len < sizeof(gdir)) && (gdir[len-1] == '/')) - gdir[len-1] = 0; + handle = FS_GetFileByHandle(f); - strncpy(fs_gamedir,gdir,sizeof(fs_gamedir)-1); - fs_gamedir[sizeof(fs_gamedir)-1] = 0; - - FS_AddGameDirectory (gdir); - } + if (handle->file) + return ftell(handle->file); + else if (handle->zip) + return unztell(handle->zip); + + return 0; } /* -============ -FS_Gamedir - -Called to find where to write a file (demos, savegames, etc) -============ -*/ -char *FS_Gamedir (void) + * ================= + * FS_ListPak + * + * Generates a listing of the contents of a pak file. + * ================= + */ +char ** +FS_ListPak(char *find, int *num) { - return fs_gamedir; -} + char **list = 0; /* File list. */ + int nfiles = 0; /* Number of files. */ + int nfound = 0; /* Number of files found. */ + int i; /* Loop counter. */ + fsPack_t *pak; /* Pak file. */ + fsSearchPath_t *search; /* Search path. */ -/* -============= -FS_ExecAutoexec -============= -*/ -void FS_ExecAutoexec (void) -{ - char name [MAX_QPATH]; - searchpath_t *s, *end; - // don't look in baseq2 if gamedir is set - if (fs_searchpaths == fs_base_searchpaths) - end = NULL; - else - end = fs_base_searchpaths; - - // search through all the paths for an autoexec.cfg file - for (s = fs_searchpaths ; s != end ; s = s->next) - { - if (s->pack) + /* Check pak files. */ + for (search = fs_searchPaths; search != NULL; search = search->next) { + if (search->pack == NULL) continue; - Com_sprintf(name, sizeof(name), "%s/autoexec.cfg", s->filename); + pak = search->pack; - if (Sys_FindFirst(name, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM)) - { - Cbuf_AddText ("exec autoexec.cfg\n"); - Sys_FindClose(); + /* Find and build list. */ + for (i = 0; i < pak->numFiles; i++) + nfiles++; + } + + list = calloc(nfiles, sizeof(char *)); + + for (search = fs_searchPaths; search != NULL; search = search->next) { + if (search->pack == NULL) + continue; + + pak = search->pack; + + /* Find and build list. */ + for (i = 0; i < pak->numFiles; i++) { + if (strstr(pak->files[i].name, find)) { + list[nfound] = strdup(pak->files[i].name); + nfound++; + } + } + } + + *num = nfound; + list = realloc(list, sizeof(char *) * nfound); + + return (list); +} + +/* + * ================= + * FS_Seek + * ================= + */ +void +FS_Seek(fileHandle_t f, int offset, fsOrigin_t origin) +{ + byte dummy[0x8000]; /* Dummy buffer to skip bytes. */ + int len; /* Length of byte chunk to skip. */ + int r; /* Number of bytes read. */ + int remaining = 0; /* Remaining bytes to skip. */ + fsHandle_t *handle; /* File handle. */ + unz_file_info info; /* ZIP information. */ + + handle = FS_GetFileByHandle(f); + + if (handle->file) { + switch (origin) { + case FS_SEEK_SET: + fseek(handle->file, offset, SEEK_SET); + break; + case FS_SEEK_CUR: + fseek(handle->file, offset, SEEK_CUR); + break; + case FS_SEEK_END: + fseek(handle->file, offset, SEEK_END); + break; + default: + Com_Error(ERR_FATAL, "FS_Seek: bad origin (%i)", origin); break; } - Sys_FindClose(); - } -} - - -/* -================ -FS_SetGamedir - -Sets the gamedir and path to a different directory. - -================ -*/ -void FS_SetGamedir (char *dir) -{ - searchpath_t *next; - - if (strstr(dir, "..") || strstr(dir, "/") - || strstr(dir, "\\") || strstr(dir, ":") ) - { - Com_Printf ("Gamedir should be a single filename, not a path\n"); - return; - } - - // - // free up any current game dir info - // - while (fs_searchpaths != fs_base_searchpaths) - { - if (fs_searchpaths->pack) - { - fclose (fs_searchpaths->pack->handle); - Z_Free (fs_searchpaths->pack->files); - Z_Free (fs_searchpaths->pack); + } else if (handle->zip) { + switch (origin) { + case FS_SEEK_SET: + remaining = offset; + break; + case FS_SEEK_CUR: + remaining = offset + unztell(handle->zip); + break; + case FS_SEEK_END: + unzGetCurrentFileInfo(handle->zip, &info, NULL, 0, NULL, 0, NULL, 0); + remaining = offset + info.uncompressed_size; + break; + default: + Com_Error(ERR_FATAL, "FS_Seek: bad origin (%i)", origin); + break; } - next = fs_searchpaths->next; - Z_Free (fs_searchpaths); - fs_searchpaths = next; - } - // - // flush all data, so it will be forced to reload - // + /* Reopen the file. */ + unzCloseCurrentFile(handle->zip); + unzOpenCurrentFile(handle->zip); -#ifndef DEDICATED_ONLY - if (dedicated && !dedicated->value) - Cbuf_AddText ("vid_restart\nsnd_restart\n"); -#endif - - // now add new entries for - if (!strcmp(dir,BASEDIRNAME) || (*dir == 0)) - { - Cvar_FullSet ("gamedir", "", CVAR_SERVERINFO|CVAR_NOSET); - Cvar_FullSet ("game", "", CVAR_LATCH|CVAR_SERVERINFO); - } - else - { - Cvar_FullSet ("gamedir", dir, CVAR_SERVERINFO|CVAR_NOSET); - if (fs_cddir->string[0]) - FS_AddGameDirectory (va("%s/%s", fs_cddir->string, dir) ); - FS_AddGameDirectory (va("%s/%s", fs_basedir->string, dir) ); - FS_AddHomeAsGameDirectory(dir); + /* Skip until the desired offset is reached. */ + while (remaining) { + len = remaining; + if (len > sizeof(dummy)) + len = sizeof(dummy); + r = unzReadCurrentFile(handle->zip, dummy, len); + if (r <= 0) + break; + remaining -= r; + } } } +/* + * ================= + * FS_Tell + * + * Returns -1 if an error occurs. + * ================= + */ +int +FS_Tell(fileHandle_t f) +{ + fsHandle_t *handle; + + handle = FS_GetFileByHandle(f); + + if (handle->file) + return (ftell(handle->file)); + else if (handle->zip) + return (unztell(handle->zip)); + + return (-1); +} /* -================ -FS_Link_f - -Creates a filelink_t -================ -*/ -void FS_Link_f (void) + * ================= + * FS_FileExists + * ================ + */ +qboolean +FS_FileExists(char *path) { - filelink_t *l, **prev; + fileHandle_t f; - if (Cmd_Argc() != 3) - { - Com_Printf ("USAGE: link \n"); + FS_FOpenFile(path, &f, FS_READ); + + if (f != 0) { + FS_FCloseFile(f); + return (true); + } + return (false); +} + +/* + * ================= + * FS_RenameFile + * ================= + */ +void +FS_RenameFile(const char *oldPath, const char *newPath) +{ + FS_DPrintf("FS_RenameFile(%s, %s)\n", oldPath, newPath); + + if (rename(oldPath, newPath)) + FS_DPrintf("FS_RenameFile: failed to rename '%s' to '%s'.\n", oldPath, newPath); +} + +/* + * ================= + * FS_DeleteFile + * ================= + */ +void +FS_DeleteFile(const char *path) +{ + FS_DPrintf("FS_DeleteFile(%s)\n", path); + + if (remove(path)) + FS_DPrintf("FS_DeleteFile: failed to delete '%s'.\n", path); +} + +/* + * Filename are reletive to the quake search path. A null buffer will just + * return the file length without loading. + */ +int +FS_LoadFile(char *path, void **buffer) +{ + byte *buf; /* Buffer. */ + int size; /* File size. */ + fileHandle_t f; /* File handle. */ + + buf = NULL; + size = FS_FOpenFile(path, &f, FS_READ); + + if (size <= 0) { + if (buffer) + *buffer = NULL; + return (size); + } + if (buffer == NULL) { + FS_FCloseFile(f); + return (size); + } + buf = Z_Malloc(size); + *buffer = buf; + + FS_Read(buf, size, f); + FS_FCloseFile(f); + + return (size); +} + +/* + * ============= + * FS_FreeFile + * ============= + */ +void +FS_FreeFile(void *buffer) +{ + if (buffer == NULL) { + FS_DPrintf("FS_FreeFile: NULL buffer.\n"); return; } + Z_Free(buffer); +} - // see if the link already exists +/* + * FS_LoadPAK + * + * 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. + */ +fsPack_t * +FS_LoadPAK(const char *packPath) +{ + int i; /* Loop counter. */ + int numFiles; /* Number of files in PAK. */ + FILE *handle; /* File handle. */ + fsPackFile_t *files; /* List of files in PAK. */ + fsPack_t *pack; /* PAK file. */ + dpackheader_t header; /* PAK file header. */ + dpackfile_t info[MAX_FILES_IN_PACK]; /* PAK info. */ + + handle = fopen(packPath, "r"); + if (handle == NULL) + return (NULL); + + fread(&header, 1, sizeof(dpackheader_t), handle); + + if (LittleLong(header.ident) != IDPAKHEADER) { + fclose(handle); + Com_Error(ERR_FATAL, "FS_LoadPAK: '%s' is not a pack file", packPath); + } + header.dirofs = LittleLong(header.dirofs); + header.dirlen = LittleLong(header.dirlen); + + numFiles = header.dirlen / sizeof(dpackfile_t); + if (numFiles > MAX_FILES_IN_PACK || numFiles == 0) { + fclose(handle); + Com_Error(ERR_FATAL, "FS_LoadPAK: '%s' has %i files", packPath, numFiles); + } + files = Z_Malloc(numFiles * sizeof(fsPackFile_t)); + + fseek(handle, header.dirofs, SEEK_SET); + fread(info, 1, header.dirlen, handle); + + /* Parse the directory. */ + for (i = 0; i < numFiles; i++) { + strncpy(files[i].name, info[i].name, sizeof(files[i].name)); + files[i].offset = LittleLong(info[i].filepos); + files[i].size = LittleLong(info[i].filelen); + } + + pack = Z_Malloc(sizeof(fsPack_t)); + strncpy(pack->name, packPath, sizeof(pack->name)); + pack->pak = handle; + pack->pk3 = NULL; + pack->numFiles = numFiles; + pack->files = files; + + Com_Printf("Added packfile '%s' (%i files).\n", pack, numFiles); + + return (pack); +} + +/* + * ================= + * FS_LoadPK3 + * + * Takes an explicit (not game tree related) path to a pack file. + * + * Loads the header and directory, adding the files at the beginning of the list + * so they override previous pack files. + * ================= + */ +fsPack_t * +FS_LoadPK3(const char *packPath) +{ + char fileName[MAX_QPATH]; /* File name. */ + int i = 0; /* Loop counter. */ + int numFiles; /* Number of files in PK3. */ + int status; /* Error indicator. */ + fsPackFile_t *files; /* List of files in PK3. */ + fsPack_t *pack; /* PK3 file. */ + unzFile *handle; /* Zip file handle. */ + unz_file_info info; /* Zip file info. */ + unz_global_info global; /* Zip file global info. */ + + handle = unzOpen(packPath); + if (handle == NULL) + return NULL; + + if (unzGetGlobalInfo(handle, &global)!=UNZ_OK) { + unzClose(handle); + Com_Error(ERR_FATAL, "FS_LoadPK3: '%s' is not a pack file", packPath); + } + numFiles = global.number_entry; + + if (numFiles > MAX_FILES_IN_PACK || numFiles == 0) { + unzClose(handle); + Com_Error(ERR_FATAL, "FS_LoadPK3: '%s' has %i files", packPath, numFiles); + } + files = Z_Malloc(numFiles * sizeof(fsPackFile_t)); + + /* Parse the directory. */ + status = unzGoToFirstFile(handle); + while (status == UNZ_OK) { + fileName[0] = '\0'; + unzGetCurrentFileInfo(handle, &info, fileName, MAX_QPATH, NULL, 0, NULL, 0); + strncpy(files[i].name, fileName, sizeof(files[i].name)); + files[i].offset = -1; /* Not used in ZIP files */ + files[i].size = info.uncompressed_size; + i++; + status = unzGoToNextFile(handle); + } + + pack = Z_Malloc(sizeof(fsPack_t)); + strncpy(pack->name, packPath, sizeof(pack->name)); + pack->pak = NULL; + pack->pk3 = handle; + pack->numFiles = numFiles; + pack->files = files; + + Com_Printf("Added packfile '%s' (%i files).\n", pack, numFiles); + + return (pack); +} + +/* + * ================ FS_AddGameDirectory + * + * Adds the directory to the head of the path, then loads and adds pak1.pak + * pak2.pak ... + * + * Extended all functionality to include Quake III .pk3 + * + * ================ + */ +void +FS_AddGameDirectory(const char *dir) +{ + char **list; /* File list. */ + char path[MAX_OSPATH]; /* Path to PAK / PK3. */ + int i, j; /* Loop counters. */ + int nfiles; /* Number of files in list. */ + fsSearchPath_t *search; /* Search path. */ + fsPack_t *pack; /* PAK / PK3 file. */ + + pack = NULL; + + /* Set game directory. */ + strncpy(fs_gamedir, dir, sizeof(fs_gamedir)); + + /* Create directory if it does not exist. */ + Sys_Mkdir(fs_gamedir); + + /* Add the directory to the search path. */ + search = Z_Malloc(sizeof(fsSearchPath_t)); + strncpy(search->path, dir, sizeof(search->path)); + search->next = fs_searchPaths; + fs_searchPaths = search; + + /* Add numbered pack files in sequence. */ + for (i = 0; i < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); i++) + for (j = 0; j < MAX_PAKS; j++) { + Com_sprintf(path, sizeof(path), "%s/pak%d.%s", dir, j, fs_packtypes[i].suffix); + + switch (fs_packtypes[i].format) { + case PAK: + pack = FS_LoadPAK(path); + break; + case PK3: + pack = FS_LoadPK3(path); + break; + } + + if (pack == NULL) + continue; + search = Z_Malloc(sizeof(fsSearchPath_t)); + search->pack = pack; + search->next = fs_searchPaths; + fs_searchPaths = search; + } + + /* Add not numbered pack files. */ + for (i = 0; i < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); i++) { + Com_sprintf(path, sizeof(path), "%s/*.%s", dir, fs_packtypes[i].suffix); + + if ((list = FS_ListFiles(path, &nfiles, 0, SFF_SUBDIR)) == NULL) + continue; + + Com_sprintf(path, sizeof(path), "%s/pak*.%s", dir, fs_packtypes[i].suffix); + + for (j = 0; j < nfiles - 1; j++) { + /* Skip numbered packs. */ + if (glob_match(path, list[j])) + continue; + + switch (fs_packtypes[i].format) { + case PAK: + pack = FS_LoadPAK(list[j]); + break; + case PK3: + pack = FS_LoadPK3(list[j]); + break; + } + + if (pack == NULL) + continue; + search = Z_Malloc(sizeof(fsSearchPath_t)); + search->pack = pack; + search->next = fs_searchPaths; + fs_searchPaths = search; + } + + FS_FreeList(list, nfiles); + } +} + +/* + * ================ + * FS_AddHomeAsGameDirectory + * + * Use ~/.quake2/dir as fs_gamedir. + * ================ + */ +void +FS_AddHomeAsGameDirectory(char *dir) +{ + char gdir[MAX_OSPATH]; /* Game directory. */ + char *homedir; /* Home directory. */ + + if ((homedir = getenv("HOME")) != NULL) { + Com_sprintf(gdir, sizeof(gdir), "%s/.quake2/%s", homedir, dir); + FS_AddGameDirectory(gdir); + } +} + +/* + * ================= + * FS_NextPath + * + * Allows enumerating all of the directories in the search path. + * ================= + */ +char * +FS_NextPath(char *prevPath) +{ + char *prev; + fsSearchPath_t *search; + + if (prevPath == NULL) + return (fs_gamedir); + + prev = fs_gamedir; + for (search = fs_searchPaths; search; search = search->next) { + if (search->pack != NULL) + continue; + + if (prevPath == prev) + return (search->path); + + prev = search->path; + } + + return (NULL); +} + +/* + * ================= + * FS_Path_f + * ================= + */ +void +FS_Path_f(void) +{ + int i; + int totalFiles = 0; + fsSearchPath_t *search; + fsHandle_t *handle; + fsLink_t *link; + + Com_Printf("Current search path:\n"); + + for (search = fs_searchPaths; search; search = search->next) { + if (search->pack != NULL) { + Com_Printf("%s (%i files)\n", search->pack->name, search->pack->numFiles); + totalFiles += search->pack->numFiles; + } else + Com_Printf("%s\n", search->path); + } + + Com_Printf("\n"); + + for (i = 0, handle = fs_handles; i < MAX_HANDLES; i++, handle++) + if (handle->file != NULL || handle->zip != NULL) + Com_Printf("Handle %i: '%s'.\n", i + 1, handle->name); + + for (i = 0, link = fs_links; link; i++, link = link->next) + Com_Printf("Link %i: '%s' -> '%s'.\n", i, link->from, link->to); + + Com_Printf("----------------------\n"); + Com_Printf("%i files in PAK/PK2/PK3/ZIP files.\n", totalFiles); +} + +/* + * ================= + * FS_Startup + * ================= + */ +void +FS_Startup(void) +{ + int i; + fsSearchPath_t *next; + fsPack_t *pack; + + if (strstr(fs_gamedirvar->string, "..") || + strstr(fs_gamedirvar->string, ".") || + strstr(fs_gamedirvar->string, "/") || + fs_gamedirvar->string[0] == '\0') { + Com_Printf("Invalid game directory.\n"); + Cvar_ForceSet("fs_game", BASEDIRNAME); + } + /* Check for game override. */ + if (strcasecmp(fs_gamedirvar->string, fs_currentGame) != 0) { + /* Free up any current game dir info. */ + while (fs_searchPaths != fs_baseSearchPaths) { + if (fs_searchPaths->pack != NULL) { + pack = fs_searchPaths->pack; + if (pack->pak != NULL) + fclose(pack->pak); + if (pack->pk3 != NULL) + unzClose(pack->pk3); + Z_Free(pack->files); + Z_Free(pack); + } + next = fs_searchPaths->next; + Z_Free(fs_searchPaths); + fs_searchPaths = next; + } + + /* Close open files for game dir. */ + for (i = 0; i < MAX_HANDLES; i++) + if (strstr(fs_handles[i].name, fs_currentGame) && + (fs_handles[i].file != NULL || fs_handles[i].zip != NULL)) + FS_FCloseFile(i); + + /* Don't add baseq2 again. */ + if (Q_stricmp(fs_gamedirvar->string, BASEDIRNAME) == 0) + strncpy(fs_gamedir, fs_basedir->string, sizeof(fs_gamedir)); + else + /* Add the directories. */ + FS_AddGameDirectory(va("%s/%s", fs_homepath->string, fs_gamedirvar->string)); + } + strncpy(fs_currentGame, fs_gamedirvar->string, sizeof(fs_currentGame)); + + FS_Path_f(); +} + +/* + * ============= + * FS_ExecAutoexec + * ============= + */ +void +FS_ExecAutoexec(void) +{ + char *dir; + char name[MAX_QPATH]; + + dir = (char *)Cvar_VariableString("gamedir"); + + if (dir[0] != '\0') + Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, dir); + else + Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, BASEDIRNAME); + + if (Sys_FindFirst(name, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) != NULL) + Cbuf_AddText("exec autoexec.cfg\n"); + + Sys_FindClose(); +} + +/* + * ================ + * FS_SetGamedir + * + * Sets the gamedir and path to a different directory. + * ================ + */ +void +FS_SetGamedir(char *dir) +{ + int i; + fsSearchPath_t *next; + + if (strstr(dir, "..") || strstr(dir, "/")) { + Com_Printf("Gamedir should be a single filename, not a path.\n"); + return; + } + /* Free up any current game dir info. */ + while (fs_searchPaths != fs_baseSearchPaths) { + if (fs_searchPaths->pack) { + if (fs_searchPaths->pack->pak) + fclose(fs_searchPaths->pack->pak); + if (fs_searchPaths->pack->pk3) + unzClose(fs_searchPaths->pack->pk3); + Z_Free(fs_searchPaths->pack->files); + Z_Free(fs_searchPaths->pack); + } + next = fs_searchPaths->next; + Z_Free(fs_searchPaths); + fs_searchPaths = next; + } + + /* Close open files for game dir. */ + for (i = 0; i < MAX_HANDLES; i++) + if (strstr(fs_handles[i].name, dir) && + (fs_handles[i].file != NULL || fs_handles[i].zip != NULL)) + FS_FCloseFile(i); + + /* Flush all data, so it will be forced to reload. */ + if (dedicated != NULL && dedicated->value != 1) + Cbuf_AddText("vid_restart\nsnd_restart\n"); + + Com_sprintf(fs_gamedir, sizeof(fs_gamedir), "%s/%s", fs_basedir->string, dir); + + if (strcmp(dir, BASEDIRNAME) == 0 || (*dir == 0)) { + Cvar_FullSet("gamedir", "", CVAR_SERVERINFO | CVAR_NOSET); + Cvar_FullSet("game", "", CVAR_LATCH | CVAR_SERVERINFO); + } else { + Cvar_FullSet("gamedir", dir, CVAR_SERVERINFO | CVAR_NOSET); + if (fs_cddir->string[0] == '\0') + FS_AddGameDirectory(va("%s/%s", fs_cddir->string, dir)); +#ifdef LIBDIR + FS_AddGameDirectory(va("%s/%s", LIBDIR, dir)); +#endif + FS_AddGameDirectory(va("%s/%s", fs_basedir->string, dir)); +#ifdef DATADIR + FS_AddHomeAsGameDirectory(dir); +#endif + } +} + +/* + * ================ + * FS_Link_f + * + * Creates a filelink_t. + * ================ + */ +void +FS_Link_f(void) +{ + fsLink_t *l, **prev; + + if (Cmd_Argc() != 3) { + Com_Printf("USAGE: link \n"); + return; + } + /* See if the link already exists. */ prev = &fs_links; - for (l=fs_links ; l ; l=l->next) - { - if (!strcmp (l->from, Cmd_Argv(1))) - { - Z_Free (l->to); - if (!strlen(Cmd_Argv(2))) - { // delete it + for (l = fs_links; l != NULL; l = l->next) { + if (strcmp(l->from, Cmd_Argv(1)) == 0) { + Z_Free(l->to); + if (strlen(Cmd_Argv(2)) == 0) { + /* Delete it. */ *prev = l->next; - Z_Free (l->from); - Z_Free (l); + Z_Free(l->from); + Z_Free(l); return; } - l->to = CopyString (Cmd_Argv(2)); + l->to = CopyString(Cmd_Argv(2)); return; } prev = &l->next; } - // create a new link + /* Create a new link. */ l = Z_Malloc(sizeof(*l)); l->next = fs_links; fs_links = l; l->from = CopyString(Cmd_Argv(1)); - l->fromlength = strlen(l->from); + l->length = strlen(l->from); l->to = CopyString(Cmd_Argv(2)); } /* -** FS_ListFiles -*/ -char **FS_ListFiles( char *findname, int *numfiles, unsigned musthave, unsigned canthave ) + * FS_ListFiles + * + * Create a list of files that match a criteria. + * + * NEW: free the list after using it with FS_FreeList() to avoid memory leaks. + */ +char ** +FS_ListFiles(char *findname, int *numfiles, unsigned musthave, unsigned canthave) { - char *s; - int nfiles = 0; - char **list = 0; + char **list; /* List of files. */ + char *s; /* Next file in list. */ + int nfiles; /* Number of files in list. */ - s = Sys_FindFirst( findname, musthave, canthave ); - while ( s ) - { - if ( s[strlen(s)-1] != '.' ) + /* Initialize variables. */ + list = NULL; + nfiles = 0; + + /* Count the number of matches. */ + s = Sys_FindFirst(findname, musthave, canthave); + while (s != NULL) { + if (s[strlen(s) - 1] != '.') nfiles++; - s = Sys_FindNext( musthave, canthave ); + s = Sys_FindNext(musthave, canthave); } - Sys_FindClose (); + Sys_FindClose(); - if ( !nfiles ) - return NULL; + /* Check if there are matches. */ + if (nfiles == 0) + return (NULL); - nfiles++; // add space for a guard + nfiles++; /* Add space for a guard. */ *numfiles = nfiles; - list = malloc( sizeof( char * ) * nfiles ); - memset( list, 0, sizeof( char * ) * nfiles ); + /* Allocate the list. */ + list = calloc(nfiles, sizeof(char *)); - s = Sys_FindFirst( findname, musthave, canthave ); + /* Fill the list. */ + s = Sys_FindFirst(findname, musthave, canthave); nfiles = 0; - while ( s ) - { - if ( s[strlen(s)-1] != '.' ) - { - list[nfiles] = strdup( s ); + while (s) { + if (s[strlen(s) - 1] != '.') { + list[nfiles] = strdup(s); nfiles++; } - s = Sys_FindNext( musthave, canthave ); + s = Sys_FindNext(musthave, canthave); } - Sys_FindClose (); + Sys_FindClose(); - return list; + return (list); } /* -** FS_Dir_f -*/ -void FS_Dir_f( void ) + * CompareAttributesPack + * + * Compare file attributes (musthave and canthave) in packed files. If + * "output" is not NULL, "size" is greater than zero and the file matches the + * attributes then a copy of the matching string will be placed there (with + * SFF_SUBDIR it changes). + * + * Returns a boolean value, true if the attributes match the file. + */ +qboolean +ComparePackFiles(const char *findname, const char *name, + unsigned musthave, unsigned canthave, char *output, int size) { - char *path = NULL; - char findname[1024]; - char wildcard[1024] = "*.*"; - char **dirnames; - int ndirs; + qboolean retval; + char *ptr; + char buffer[MAX_OSPATH]; - if ( Cmd_Argc() != 1 ) - { - strcpy( wildcard, Cmd_Argv( 1 ) ); - } + strncpy(buffer, name, sizeof(buffer)); - while ( ( path = FS_NextPath( path ) ) != NULL ) - { - char *tmp = findname; + if ((canthave & SFF_SUBDIR) && name[strlen(name)-1] == '/') + return (false); - Com_sprintf( findname, sizeof(findname), "%s/%s", path, wildcard ); - - while ( *tmp != 0 ) - { - if ( *tmp == '\\' ) - *tmp = '/'; - tmp++; - } - Com_Printf( "Directory of %s\n", findname ); - Com_Printf( "----\n" ); - - if ( ( dirnames = FS_ListFiles( findname, &ndirs, 0, 0 ) ) != 0 ) - { - int i; - - for ( i = 0; i < ndirs-1; i++ ) - { - if ( strrchr( dirnames[i], '/' ) ) - Com_Printf( "%s\n", strrchr( dirnames[i], '/' ) + 1 ); - else - Com_Printf( "%s\n", dirnames[i] ); - - free( dirnames[i] ); - } - free( dirnames ); - } - Com_Printf( "\n" ); - }; -} - -/* -============ -FS_Path_f - -============ -*/ -void FS_Path_f (void) -{ - searchpath_t *s; - filelink_t *l; - - Com_Printf ("Current search path:\n"); - for (s=fs_searchpaths ; s ; s=s->next) - { - if (s == fs_base_searchpaths) - Com_Printf ("----------\n"); - if (s->pack) - Com_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); + if (musthave & SFF_SUBDIR) { + if ((ptr = strrchr(buffer, '/')) != NULL) + *ptr = '\0'; else - Com_Printf ("%s\n", s->filename); + return (false); } - Com_Printf ("\nLinks:\n"); - for (l=fs_links ; l ; l=l->next) - Com_Printf ("%s : %s\n", l->from, l->to); + if ((musthave & SFF_HIDDEN) || (canthave & SFF_HIDDEN)) { + if ((ptr = strrchr(buffer, '/')) == NULL) + ptr = buffer; + if (((musthave & SFF_HIDDEN) && ptr[1] != '.') || + ((canthave & SFF_HIDDEN) && ptr[1] == '.')) + return (false); + } + + if (canthave & SFF_RDONLY) + return (false); + + retval = glob_match((char *)findname, buffer); + + if (retval && output != NULL) + strncpy(output, buffer, size); + + return (retval); } /* -================ -FS_NextPath - -Allows enumerating all of the directories in the search path -================ -*/ -char *FS_NextPath (char *prevpath) + * FS_ListFiles2 + * + * Create a list of files that match a criteria. + * + * Searchs are relative to the game directory and use all the search paths + * including .pak and .pk3 files. + */ +char ** +FS_ListFiles2(char *findname, int *numfiles, unsigned musthave, unsigned canthave) { - searchpath_t *s; - char *prev; + fsSearchPath_t *search; /* Search path. */ + int i, j; /* Loop counters. */ + int nfiles; /* Number of files found. */ + int tmpnfiles; /* Temp number of files. */ + char **tmplist; /* Temporary list of files. */ + char **list; /* List of files found. */ + char path[MAX_OSPATH]; /* Temporary path. */ - prev = NULL; /* fs_gamedir is the first directory in the searchpath */ - for (s=fs_searchpaths ; s ; s=s->next) - { - if (s->pack) + nfiles = 0; + list = malloc(sizeof(char *)); + + for (search = fs_searchPaths; search != NULL; search = search->next) { + if (search->pack != NULL) { + if (canthave & SFF_INPACK) + continue; + + for (i = 0, j = 0; i < search->pack->numFiles; i++) + if (ComparePackFiles(findname, + search->pack->files[i].name, + musthave, canthave, NULL, 0)) + j++; + if (j == 0) + continue; + nfiles += j; + list = realloc(list, nfiles * sizeof(char *)); + for (i = 0, j = nfiles - j; + i < search->pack->numFiles; + i++) + if (ComparePackFiles(findname, + search->pack->files[i].name, + musthave, canthave, path, sizeof(path))) + list[j++] = strdup(path); + } else if (search->path != NULL) { + if (musthave & SFF_INPACK) + continue; + + Com_sprintf(path, sizeof(path), "%s/%s", + search->path, findname); + tmplist = FS_ListFiles(path, &tmpnfiles, musthave, + canthave); + if (tmplist != NULL) { + tmpnfiles--; + nfiles += tmpnfiles; + list = realloc(list, nfiles * sizeof(char *)); + for (i = 0, j = nfiles - tmpnfiles; + i < tmpnfiles; + i++, j++) + list[j] = strdup(tmplist[i] + + strlen(search->path) + 1); + FS_FreeList(tmplist, tmpnfiles + 1); + } + } + } + + /* Delete duplicates. */ + tmpnfiles = 0; + for (i = 0; i < nfiles; i++) { + if (list[i] == NULL) continue; - if (prevpath == NULL) - return s->filename; - if (prevpath == prev) - return s->filename; - prev = s->filename; + for (j = i + 1; j < nfiles; j++) + if (list[j] != NULL && + strcmp(list[i], list[j]) == 0) { + free(list[j]); + list[j] = NULL; + tmpnfiles++; + } } - return NULL; -} + if (tmpnfiles > 0) { + nfiles -= tmpnfiles; + tmplist = malloc(nfiles * sizeof(char *)); + for (i = 0, j = 0; i < nfiles + tmpnfiles; i++) + if (list[i] != NULL) + tmplist[j++] = list[i]; + free(list); + list = tmplist; + } + /* Add a guard. */ + if (nfiles > 0) { + nfiles++; + list = realloc(list, nfiles * sizeof(char *)); + list[nfiles - 1] = NULL; + } else { + free(list); + list = NULL; + } + + *numfiles = nfiles; + + return (list); +} /* -================ -FS_InitFilesystem -================ -*/ -void FS_InitFilesystem (void) + * FS_FreeList + * + * Free list of files created by FS_ListFiles(). + */ +void +FS_FreeList(char **list, int nfiles) { - Cmd_AddCommand ("path", FS_Path_f); - Cmd_AddCommand ("link", FS_Link_f); - Cmd_AddCommand ("dir", FS_Dir_f ); + int i; - // - // basedir - // allows the game to run from outside the data tree - // - fs_basedir = Cvar_Get ("basedir", ".", CVAR_NOSET); + for (i = 0; i < nfiles - 1; i++) + free(list[i]); - // - // cddir - // Logically concatenates the cddir after the basedir for - // allows the game to run from outside the data tree - // - fs_cddir = Cvar_Get ("cddir", "", CVAR_NOSET); - if (fs_cddir->string[0]) - FS_AddGameDirectory (va("%s/"BASEDIRNAME, fs_cddir->string) ); - - // - // add baseq2 to search path - // - FS_AddGameDirectory (va("%s/"BASEDIRNAME, fs_basedir->string) ); - - // - // then add a '.quake2/baseq2' directory in home directory by default - // - FS_AddHomeAsGameDirectory(BASEDIRNAME); - - // any set gamedirs will be freed up to here - fs_base_searchpaths = fs_searchpaths; - - // check for game override - fs_gamedirvar = Cvar_Get ("game", "", CVAR_LATCH|CVAR_SERVERINFO); - if (fs_gamedirvar->string[0]) - FS_SetGamedir (fs_gamedirvar->string); + free(list); +} + +/* + * ================ + * FS_Dir_f + * + * Directory listing. + * ================ + */ +void +FS_Dir_f(void) +{ + char **dirnames; /* File list. */ + char findname[1024]; /* File search path and pattern. */ + char *path = NULL; /* Search path. */ + char wildcard[1024] = "*.*"; /* File pattern. */ + int i; /* Loop counter. */ + int ndirs; /* Number of files in list. */ + + /* Check for pattern in arguments. */ + if (Cmd_Argc() != 1) + strncpy(wildcard, Cmd_Argv(1), sizeof(wildcard)); + + /* Scan search paths and list files. */ + while ((path = FS_NextPath(path)) != NULL) { + Com_sprintf(findname, sizeof(findname), "%s/%s", path, wildcard); + Com_Printf("Directory of '%s'.\n", findname); + Com_Printf("----\n"); + + if ((dirnames = FS_ListFiles(findname, &ndirs, 0, 0)) != 0) { + for (i = 0; i < ndirs - 1; i++) { + if (strrchr(dirnames[i], '/')) + Com_Printf("%s\n", strrchr(dirnames[i], '/') + 1); + else + Com_Printf("%s\n", dirnames[i]); + } + FS_FreeList(dirnames, ndirs); + } + Com_Printf("\n"); + } +} + +/* + * ================ + * FS_InitFilesystem + * ================ + */ +void +FS_InitFilesystem(void) +{ + /* Register FS commands. */ + Cmd_AddCommand("path", FS_Path_f); + Cmd_AddCommand("link", FS_Link_f); + Cmd_AddCommand("dir", FS_Dir_f); + + /* + * basedir Allows the game to run from outside the data tree. + */ +#ifdef DATADIR + fs_basedir = Cvar_Get("basedir", DATADIR, CVAR_NOSET); +#else + fs_basedir = Cvar_Get("basedir", ".", CVAR_NOSET); +#endif + + /* + * cddir Logically concatenates the cddir after the basedir to + * allow the game to run from outside the data tree. + */ + fs_cddir = Cvar_Get("cddir", "", CVAR_NOSET); + if (fs_cddir->string[0] != '\0') + FS_AddGameDirectory(va("%s/" BASEDIRNAME, fs_cddir->string)); + + /* Debug flag. */ + fs_debug = Cvar_Get("fs_debug", "0", 0); + + /* Game directory. */ + fs_gamedirvar = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO); + + /* Current directory. */ + fs_homepath = Cvar_Get("homepath", Sys_GetCurrentDirectory(), CVAR_NOSET); + + /* Add baseq2 to search path. */ +#ifdef LIBDIR + FS_AddGameDirectory(va("%s/" BASEDIRNAME, LIBDIR)); +#endif + FS_AddGameDirectory(va("%s/" BASEDIRNAME, fs_basedir->string)); +#ifdef DATADIR + FS_AddHomeAsGameDirectory(BASEDIRNAME); +#endif + + /* Any set gamedirs will be freed up to here. */ + fs_baseSearchPaths = fs_searchPaths; + strncpy(fs_currentGame, BASEDIRNAME, sizeof(fs_currentGame)); + + /* Check for game override. */ + if (fs_gamedirvar->string[0] != '\0') + FS_SetGamedir(fs_gamedirvar->string); + + /* Create directory if it does not exist. */ + Sys_Mkdir(fs_gamedir); + + Com_Printf("Using '%s' for writing.\n", fs_gamedir); +} + +/* + * ================= + * FS_Shutdown + * ================= + */ +void +FS_Shutdown(void) +{ + int i; + fsHandle_t *handle; + fsSearchPath_t *next; + fsPack_t *pack; + + /* Unregister commands. */ + Cmd_RemoveCommand("dir"); + Cmd_RemoveCommand("link"); + Cmd_RemoveCommand("path"); + + /* Close all files. */ + for (i = 0, handle = fs_handles; i < MAX_HANDLES; i++, handle++) { + if (handle->file != NULL) + fclose(handle->file); + if (handle->zip != NULL) { + unzCloseCurrentFile(handle->zip); + unzClose(handle->zip); + } + } + + /* Free the search paths. */ + while (fs_searchPaths != NULL) { + if (fs_searchPaths->pack != NULL) { + pack = fs_searchPaths->pack; + if (pack->pak != NULL) + fclose(pack->pak); + if (pack->pk3 != NULL) + unzClose(pack->pk3); + Z_Free(pack->files); + Z_Free(pack); + } + next = fs_searchPaths->next; + Z_Free(fs_searchPaths); + fs_searchPaths = next; + } } diff --git a/src/common/qcommon.h b/src/common/qcommon.h index 844c26dc..18ce2ee1 100644 --- a/src/common/qcommon.h +++ b/src/common/qcommon.h @@ -660,27 +660,71 @@ FILESYSTEM ============================================================== */ -void FS_InitFilesystem (void); -void FS_SetGamedir (char *dir); -char *FS_Gamedir (void); -char *FS_NextPath (char *prevpath); -void FS_ExecAutoexec (void); +#define SFF_INPACK 0x20 /* For FS_ListFiles2(). */ -int FS_FOpenFile (char *filename, FILE **file); -void FS_FCloseFile (FILE *f); -// note: this can't be called from another DLL, due to MS libc issues +extern int file_from_pak; /* ZOID did file come from pak? */ -int FS_LoadFile (char *path, void **buffer); -// a null buffer will just return the file length without loading -// a -1 length is not present +typedef int fileHandle_t; -void FS_Read (void *buffer, int len, FILE *f); -// properly handles partial reads +typedef enum { + FS_READ, + FS_WRITE, + FS_APPEND +} fsMode_t; -void FS_FreeFile (void *buffer); +typedef enum { + FS_SEEK_CUR, + FS_SEEK_SET, + FS_SEEK_END +} fsOrigin_t; -void FS_CreatePath (char *path); +typedef enum { + FS_SEARCH_PATH_EXTENSION, + FS_SEARCH_BY_FILTER, + FS_SEARCH_FULL_PATH +} fsSearchType_t; +void FS_Startup(void); +void FS_Shutdown(void); +void FS_DPrintf(const char *format,...); +FILE *FS_FileForHandle(fileHandle_t f); +int FS_FOpenFile(const char *name, fileHandle_t * f, fsMode_t mode); +void FS_FCloseFile(fileHandle_t f); +int FS_Read (void *buffer, int size, fileHandle_t f); +int FS_FRead (void *buffer, int size, int count, fileHandle_t f); +int FS_Write (const void *buffer, int size, fileHandle_t f); +void FS_Seek (fileHandle_t f, int offset, fsOrigin_t origin); +int FS_FTell (fileHandle_t f); +int FS_Tell (fileHandle_t f); +qboolean FS_FileExists(char *path); +void FS_CopyFile(const char *srcPath, const char *dstPath); +void FS_RenameFile(const char *oldPath, const char *newPath); +void FS_DeleteFile(const char *path); +void FS_DeletePath(char *path); +int FS_GetFileList(const char *path, const char *extension, char *buffer, int size, fsSearchType_t searchType); +char **FS_ListPak(char *find, int *num); /* Knighmare- pak list function */ +char **FS_ListFiles(char *findname, int *numfiles, unsigned musthave, unsigned canthave); +char **FS_ListFiles2(char *findname, int *numfiles, unsigned musthave, unsigned canthave); +void FS_FreeList(char **list, int nfiles); + +void FS_InitFilesystem(void); +void FS_SetGamedir(char *dir); +char *FS_Gamedir(void); +char *FS_NextPath(char *prevpath); +void FS_ExecAutoexec(void); + +/* note: this can't be called from another DLL, due to MS libc issues */ + +int FS_LoadFile(char *path, void **buffer); + +/* a null buffer will just return the file length without loading */ +/* a -1 length is not present */ + +/* properly handles partial reads */ + +void FS_FreeFile(void *buffer); + +void FS_CreatePath(char *path); /* ============================================================== diff --git a/src/game/baseq2/q_shared.h b/src/game/baseq2/q_shared.h index 50b7ee32..7c3f5c11 100644 --- a/src/game/baseq2/q_shared.h +++ b/src/game/baseq2/q_shared.h @@ -240,6 +240,8 @@ extern int curtime; // time returned by last Sys_Milliseconds int Sys_Milliseconds (void); void Sys_Mkdir (char *path); +void Sys_Rmdir(char *path); +char *strlwr (char *s); // large block stack allocation routines void *Hunk_Begin (int maxsize); diff --git a/src/posix/posix.c b/src/posix/posix.c index 684d2097..97f5e459 100644 --- a/src/posix/posix.c +++ b/src/posix/posix.c @@ -158,6 +158,23 @@ void Sys_Mkdir (char *path) mkdir (path, 0777); } +void +Sys_Rmdir(char *path) +{ + rmdir(path); +} + +char * +Sys_GetCurrentDirectory(void) +{ + static char dir[MAX_OSPATH]; + + if (!getcwd(dir, sizeof(dir))) + Sys_Error("Couldn't get current working directory"); + + return dir; +} + char *strlwr (char *s) { char *p = s; diff --git a/src/server/sv_ccmds.c b/src/server/sv_ccmds.c index 10e7a554..48f12b95 100644 --- a/src/server/sv_ccmds.c +++ b/src/server/sv_ccmds.c @@ -317,7 +317,7 @@ void SV_ReadLevelFile (void) Com_Printf ("Failed to open %s\n", name); return; } - FS_Read (sv.configstrings, sizeof(sv.configstrings), f); + FS_Read (sv.configstrings, sizeof(sv.configstrings), (size_t)f); CM_ReadPortalState (f); fclose (f); @@ -421,10 +421,10 @@ void SV_ReadServerFile (void) return; } // read the comment field - FS_Read (comment, sizeof(comment), f); + FS_Read (comment, sizeof(comment), (size_t)f); // read the mapcmd - FS_Read (mapcmd, sizeof(mapcmd), f); + FS_Read (mapcmd, sizeof(mapcmd), (size_t)f); // read all CVAR_LATCH cvars // these will be things like coop, skill, deathmatch, etc @@ -432,7 +432,7 @@ void SV_ReadServerFile (void) { if (!fread (name, 1, sizeof(name), f)) break; - FS_Read (string, sizeof(string), f); + FS_Read (string, sizeof(string), (size_t)f); Com_DPrintf ("Set %s = %s\n", name, string); Cvar_ForceSet (name, string); } diff --git a/src/server/sv_user.c b/src/server/sv_user.c index 6e3d4d20..e344882c 100644 --- a/src/server/sv_user.c +++ b/src/server/sv_user.c @@ -42,7 +42,7 @@ void SV_BeginDemoserver (void) char name[MAX_OSPATH]; Com_sprintf (name, sizeof(name), "demos/%s", sv.name); - FS_FOpenFile (name, &sv.demofile); + FS_FOpenFile (name, (fileHandle_t *)&sv.demofile, FS_READ); if (!sv.demofile) Com_Error (ERR_DROP, "Couldn't open %s\n", name); } diff --git a/src/unzip/crypt.h b/src/unzip/crypt.h new file mode 100644 index 00000000..f3068b08 --- /dev/null +++ b/src/unzip/crypt.h @@ -0,0 +1,138 @@ +/* + * crypt.h -- base code for crypt/uncrypt ZIPfile + * + * + * Version 1.01e, February 12th, 2005 + * + * Copyright (C) 1998-2005 Gilles Vollant + * + * This code is a modified version of crypting code in Infozip distribution + * + * The encryption/decryption parts of this source code (as opposed to the + * non-echoing password parts) were originally written in Europe. The whole + * source package can be freely distributed, including from the USA. (Prior + * to January 2000, re-export from the US was a violation of US law.) + * + * This encryption code is a direct transcription of the algorithm from Roger + * Schlafly, described by Phil Katz in the file appnote.txt. This file + * (appnote.txt) is distributed with the PKZIP program (even in the version + * without encryption capabilities). + * + * If you don't need crypting in your application, just define symbols NOCRYPT + * and NOUNCRYPT. + * + * This code support the "Traditional PKWARE Encryption". + * + * The new AES encryption added on Zip format by Winzip (see the page + * http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + */ +/* Encryption is not supported. */ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int +decrypt_byte(unsigned long *pkeys, const unsigned long *pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow + * in an unpredictable manner on 16-bit + * systems; not a problem with any known + * compiler so far, though */ + + temp = ((unsigned)(*(pkeys + 2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int +update_keys(unsigned long *pkeys, const unsigned long *pcrc_32_tab, int c) +{ + (*(pkeys + 0)) = CRC32((*(pkeys + 0)), c); + (*(pkeys + 1)) += (*(pkeys + 0)) & 0xff; + (*(pkeys + 1)) = (*(pkeys + 1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys + 1)) >> 24); + + (*(pkeys + 2)) = CRC32((*(pkeys + 2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void +init_keys(const char *passwd, unsigned long *pkeys, const unsigned long *pcrc_32_tab) +{ + *(pkeys + 0) = 305419896L; + *(pkeys + 1) = 591751049L; + *(pkeys + 2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys, pcrc_32_tab, (int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 +/* "last resort" source for second part of crypt seed pattern */ +#ifndef ZCR_SEED2 +#define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +#endif + +static int +crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) + const char *passwd; /* password string */ + unsigned char *buf; /* where to write header */ + int bufSize; + unsigned long *pkeys; + const unsigned long *pcrc_32_tab; + unsigned long crcForCrypting; +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN - 2]; /* random header */ + static unsigned calls = 0; /* ensure different random header + * each time */ + + if (bufSize < RAND_HEAD_LEN) + return 0; + + /* + * First generate RAND_HEAD_LEN-2 random bytes. We encrypt the output + * of rand() to get less predictability, since rand() is often poorly + * implemented. + */ + if (++calls == 1) { + srand((unsigned)(time(NULL) ^ ZCR_SEED2)); + } + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN - 2; n++) { + c = (rand() >> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN - 2; n++) { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/src/unzip/ioapi.c b/src/unzip/ioapi.c new file mode 100644 index 00000000..b9b45cc1 --- /dev/null +++ b/src/unzip/ioapi.c @@ -0,0 +1,191 @@ +/* + * ioapi.c -- IO base function header for compress/uncompress .zip files + * using zlib + zip or unzip API + * + * Version 1.01e, February 12th, 2005 + * + * Copyright (C) 1998-2005 Gilles Vollant + */ + +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +voidpf ZCALLBACK fopen_file_func +OF(( + voidpf opaque, + const char *filename, + int mode)); + + uLong ZCALLBACK fread_file_func OF(( + voidpf opaque, + voidpf stream, + void *buf, + uLong size)); + + uLong ZCALLBACK fwrite_file_func OF(( + voidpf opaque, + voidpf stream, + const void *buf, + uLong size)); + + long ZCALLBACK ftell_file_func OF(( + voidpf opaque, + voidpf stream)); + + long ZCALLBACK fseek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + + int ZCALLBACK fclose_file_func OF(( + voidpf opaque, + voidpf stream)); + + int ZCALLBACK ferror_file_func OF(( + voidpf opaque, + voidpf stream)); + + + voidpf ZCALLBACK fopen_file_func(opaque, filename, mode) + voidpf opaque; + const char *filename; + int mode; +{ + FILE *file = NULL; + const char *mode_fopen = NULL; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename != NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + + +uLong ZCALLBACK +fread_file_func(opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void *buf; + uLong size; +{ + uLong ret; + + ret = (uLong) fread(buf, 1, (size_t) size, (FILE *) stream); + return ret; +} + + +uLong ZCALLBACK +fwrite_file_func(opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + const void *buf; + uLong size; +{ + uLong ret; + + ret = (uLong) fwrite(buf, 1, (size_t) size, (FILE *) stream); + return ret; +} + +long ZCALLBACK +ftell_file_func(opaque, stream) + voidpf opaque; + voidpf stream; +{ + long ret; + + ret = ftell((FILE *) stream); + return ret; +} + +long ZCALLBACK +fseek_file_func(opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + int fseek_origin = 0; + long ret; + + switch (origin) { + case ZLIB_FILEFUNC_SEEK_CUR: + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END: + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET: + fseek_origin = SEEK_SET; + break; + default: + return -1; + } + ret = 0; + fseek((FILE *) stream, offset, fseek_origin); + return ret; +} + +int ZCALLBACK +fclose_file_func(opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + + ret = fclose((FILE *) stream); + return ret; +} + +int ZCALLBACK +ferror_file_func(opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + + ret = ferror((FILE *) stream); + return ret; +} + +void +fill_fopen_filefunc(pzlib_filefunc_def) + zlib_filefunc_def *pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/unzip/ioapi.h b/src/unzip/ioapi.h new file mode 100644 index 00000000..7b14df16 --- /dev/null +++ b/src/unzip/ioapi.h @@ -0,0 +1,75 @@ +/* + * ioapi.h -- IO base function header for compress/uncompress .zip files + * using zlib + zip or unzip API + * + * Version 1.01e, February 12th, 2005 + * + * Copyright (C) 1998-2005 Gilles Vollant + */ + +#ifndef _ZLIBIOAPI_H +#define _ZLIBIOAPI_H + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + typedef voidpf (ZCALLBACK * open_file_func) OF((voidpf opaque, const char *filename, int mode)); + typedef uLong (ZCALLBACK * read_file_func) OF((voidpf opaque, voidpf stream, void *buf, uLong size)); + typedef uLong (ZCALLBACK * write_file_func) OF((voidpf opaque, voidpf stream, const void *buf, uLong size)); + typedef long (ZCALLBACK * tell_file_func) OF((voidpf opaque, voidpf stream)); + typedef long (ZCALLBACK * seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + typedef int (ZCALLBACK * close_file_func) OF((voidpf opaque, voidpf stream)); + typedef int (ZCALLBACK * testerror_file_func) OF((voidpf opaque, voidpf stream)); + + typedef struct zlib_filefunc_def_s { + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; + } zlib_filefunc_def; + + + + void fill_fopen_filefunc OF((zlib_filefunc_def * pzlib_filefunc_def)); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#ifdef __cplusplus +} + +#endif + +#endif diff --git a/src/unzip/unzip.c b/src/unzip/unzip.c new file mode 100644 index 00000000..319c330d --- /dev/null +++ b/src/unzip/unzip.c @@ -0,0 +1,1644 @@ +/* + * unzip.c -- IO for uncompress .zip files using zlib Version 1.01e, February + * 12th, 2005 + * + * Copyright (C) 1998-2005 Gilles Vollant + * + * Read unzip.h for more info + */ + +/* + * Decryption code comes from crypt.c by Info-ZIP but has been greatly + * reduced in terms of compatibility with older software. The following is + * from the original crypt.c. Code woven in by Terry Thorsen 1/2003. + */ + +/* + * Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + * + * See the accompanying file LICENSE, version 2000-Apr-09 or later (the contents + * of which are also included in zip.h) for terms of use. If, for some + * reason, all these files are missing, the Info-ZIP license also may be + * found at: ftp://ftp.info-zip.org/pub/infozip/license.html + */ + +/* + * crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + * + * The encryption/decryption parts of this source code (as opposed to the + * non-echoing password parts) were originally written in Europe. The whole + * source package can be freely distributed, including from the USA. (Prior + * to January 2000, re-export from the US was a violation of US law.) + */ + +/* + * This encryption code is a direct transcription of the algorithm from Roger + * Schlafly, described by Phil Katz in the file appnote.txt. This file + * (appnote.txt) is distributed with the PKZIP program (even in the version + * without encryption capabilities). + */ + + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +#include +#include +#include +#endif +#ifdef NO_ERRNO_H +extern int errno; + +#else +#include +#endif + + +#ifndef local +#define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +#define CASESENSITIVITYDEFAULT_NO +#endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +#define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +#define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = +" unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile */ +typedef struct unz_file_info_internal_s { + uLong offset_curfile; /* relative offset of local header 4 + * bytes */ +} unz_file_info_internal; + + +/* + * file_in_zip_read_info_s contain internal information about a file in + * zipfile, when reading and decompress it + */ +typedef struct { + char *read_buffer; /* internal buffer for compressed + * data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, + * for fseek */ + uLong stream_initialised; /* flag set if stream + * structure is initialised */ + + uLong offset_local_extrafield; /* offset of the local + * extra field */ + uInt size_local_extrafield; /* size of the local extra + * field */ + uLong pos_local_extrafield; /* position in the local + * extra field in read */ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after + * decompress all */ + uLong rest_read_compressed; /* number of byte to be + * decompressed */ + uLong rest_read_uncompressed; /* number of byte to be + * obtained after decomp */ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method + * (0==store) */ + uLong byte_before_the_zipfile; /* byte before the + * zipfile, (>0 for sfx) */ + int raw; +} file_in_zip_read_info_s; + + +/* + * unz_s contain internal information about the zipfile + */ +typedef struct { + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile; /* byte before the + * zipfile, (>0 for sfx) */ + uLong num_file; /* number of the current file in the + * zipfile */ + uLong pos_in_central_dir; /* pos of the current file in + * the central dir */ + uLong current_file_ok; /* flag about the usability + * of the current file */ + uLong central_pos; /* position of the beginning of the + * central dir */ + + uLong size_central_dir; /* size of the central + * directory */ + uLong offset_central_dir; /* offset of start of central + * directory with respect to + * the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file + * in zip */ + unz_file_info_internal cur_file_info_internal; /* private info about it */ + file_in_zip_read_info_s *pfile_in_zip_read; /* structure about the + * current file if we + * are decompressing it */ + int encrypted; +#ifndef NOUNCRYPT + unsigned long keys[3];/* keys defining the pseudo-random sequence */ + const unsigned long *pcrc_32_tab; +#endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* + * =========================================================================== + * Read a byte from a gz_stream; update next_in and avail_in. Return EOF for + * end of file. IN assertion: the stream s has been sucessfully opened for + * reading. + */ + + +local int unzlocal_getByte +OF(( + const zlib_filefunc_def * pzlib_filefunc_def, + voidpf filestream, + int *pi)); + + local int unzlocal_getByte(pzlib_filefunc_def, filestream, pi) + const zlib_filefunc_def *pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def, filestream, &c, 1); + + if (err == 1) { + *pi = (int)c; + return UNZ_OK; + } else { + if (ZERROR(*pzlib_filefunc_def, filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* + * =========================================================================== + * Reads a long in LSB order from the given gz_stream. Sets + */ +local int unzlocal_getShort +OF(( + const zlib_filefunc_def * pzlib_filefunc_def, + voidpf filestream, + uLong * pX)); + + local int unzlocal_getShort(pzlib_filefunc_def, filestream, pX) + const zlib_filefunc_def *pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def, filestream, &i); + x = (uLong) i; + + if (err == UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def, filestream, &i); + x += ((uLong) i) << 8; + + if (err == UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong +OF(( + const zlib_filefunc_def * pzlib_filefunc_def, + voidpf filestream, + uLong * pX)); + + local int unzlocal_getLong(pzlib_filefunc_def, filestream, pX) + const zlib_filefunc_def *pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def, filestream, &i); + x = (uLong) i; + + if (err == UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def, filestream, &i); + x += ((uLong) i) << 8; + + if (err == UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def, filestream, &i); + x += ((uLong) i) << 16; + + if (err == UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def, filestream, &i); + x += ((uLong) i) << 24; + + if (err == UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int +strcmpcasenosensitive_internal(const char *fileName1, const char *fileName2) +{ + for (;;) { + char c1 = *(fileName1++); + char c2 = *(fileName2++); + + if ((c1 >= 'a') && (c1 <= 'z')) + c1 -= 0x20; + if ((c2 >= 'a') && (c2 <= 'z')) + c2 -= 0x20; + if (c1 == '\0') + return ((c2 == '\0') ? 0 : -1); + if (c2 == '\0') + return 1; + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + * Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, + * comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, + * comparision is not case sensitivity (like strcmpi or strcasecmp) If + * iCaseSenisivity = 0, case sensitivity is defaut of your operating system + * (like 1 on Unix, 2 on Windows) + * + */ +extern int ZEXPORT +unzStringFileNameCompare(fileName1, fileName2, iCaseSensitivity) + const char *fileName1; + const char *fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity == 0) + iCaseSensitivity = CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity == 1) + return strcmp(fileName1, fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1, fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + * Locate the Central directory of a zipfile (at the end, just before the + * global comment) + */ +local uLong unzlocal_SearchCentralDir +OF(( + const zlib_filefunc_def * pzlib_filefunc_def, + voidpf filestream)); + + local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def, filestream) + const zlib_filefunc_def *pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char *buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack = 0xffff; /* maximum size of global + * comment */ + uLong uPosFound = 0; + + if (ZSEEK(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def, filestream); + + if (uMaxBack > uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char *)ALLOC(BUFREADCOMMENT + 4); + if (buf == NULL) + return 0; + + uBackRead = 4; + while (uBackRead < uMaxBack) { + uLong uReadSize, uReadPos; + int i; + + if (uBackRead + BUFREADCOMMENT > uMaxBack) + uBackRead = uMaxBack; + else + uBackRead += BUFREADCOMMENT; + uReadPos = uSizeFile - uBackRead; + + uReadSize = ((BUFREADCOMMENT + 4) < (uSizeFile - uReadPos)) ? + (BUFREADCOMMENT + 4) : (uSizeFile - uReadPos); + if (ZSEEK(*pzlib_filefunc_def, filestream, uReadPos, ZLIB_FILEFUNC_SEEK_SET) != 0) + break; + + if (ZREAD(*pzlib_filefunc_def, filestream, buf, uReadSize) != uReadSize) + break; + + for (i = (int)uReadSize - 3; (i--) > 0;) + if (((*(buf + i)) == 0x50) && ((*(buf + i + 1)) == 0x4b) && + ((*(buf + i + 2)) == 0x05) && ((*(buf + i + 3)) == 0x06)) { + uPosFound = uReadPos + i; + break; + } + if (uPosFound != 0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + * Open a Zip file. path contain the full pathname (by example, on a Windows + * NT computer "c:\\test\\zlib114.zip" or on an Unix computer + * "zlib/zlib114.zip". If the zipfile cannot be opened (file doesn't exist or + * in not valid), the return value is NULL. Else, the return value is a + * unzFile Handle, usable with other function of this unzip package. + */ +extern unzFile ZEXPORT +unzOpen2(path, pzlib_filefunc_def) + const char *path; + zlib_filefunc_def *pzlib_filefunc_def; +{ + unz_s us; + unz_s *s; + uLong central_pos, uL; + + uLong number_disk; /* number of the current dist, used + * for spaning ZIP, unsupported, + * always 0 */ + uLong number_disk_with_CD; /* number the the disk with + * central dir, used for + * spaning ZIP, unsupported, + * always 0 */ + uLong number_entry_CD; /* total number of entries in + * the central dir (same than + * number_entry on nospan) */ + + int err = UNZ_OK; + + if (unz_copyright[0] != ' ') + return NULL; + + if (pzlib_filefunc_def == NULL) + fill_fopen_filefunc(&us.z_filefunc); + else + us.z_filefunc = *pzlib_filefunc_def; + + us.filestream = (*(us.z_filefunc.zopen_file)) (us.z_filefunc.opaque, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream == NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc, us.filestream); + if (central_pos == 0) + err = UNZ_ERRNO; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream, &uL) != UNZ_OK) + err = UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream, &number_disk) != UNZ_OK) + err = UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream, &number_disk_with_CD) != UNZ_OK) + err = UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream, &us.gi.number_entry) != UNZ_OK) + err = UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream, &number_entry_CD) != UNZ_OK) + err = UNZ_ERRNO; + + if ((number_entry_CD != us.gi.number_entry) || + (number_disk_with_CD != 0) || + (number_disk != 0)) + err = UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream, &us.size_central_dir) != UNZ_OK) + err = UNZ_ERRNO; + + /* + * offset of start of central directory with respect to the starting + * disk number + */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream, &us.offset_central_dir) != UNZ_OK) + err = UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream, &us.gi.size_comment) != UNZ_OK) + err = UNZ_ERRNO; + + if ((central_pos < us.offset_central_dir + us.size_central_dir) && + (err == UNZ_OK)) + err = UNZ_BADZIPFILE; + + if (err != UNZ_OK) { + ZCLOSE(us.z_filefunc, us.filestream); + return NULL; + } + us.byte_before_the_zipfile = central_pos - + (us.offset_central_dir + us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + us.encrypted = 0; + + + s = (unz_s *) ALLOC(sizeof(unz_s)); + *s = us; + unzGoToFirstFile((unzFile) s); + return (unzFile) s; +} + + +extern unzFile ZEXPORT +unzOpen(path) + const char *path; +{ + return unzOpen2(path, NULL); +} + +/* + * Close a ZipFile opened with unzipOpen. If there is files inside the .Zip + * opened with unzipOpenCurrentFile (see later), these files MUST be closed + * with unzipCloseCurrentFile before call unzipClose. return UNZ_OK if there + * is no problem. + */ +extern int ZEXPORT +unzClose(file) + unzFile file; +{ + unz_s *s; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + * Write info about the ZipFile in the *pglobal_info structure. No + * preparation of the structure is needed return UNZ_OK if there is no + * problem. + */ +extern int ZEXPORT +unzGetGlobalInfo(file, pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s *s; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + *pglobal_info = s->gi; + return UNZ_OK; +} + + +/* + * Translate date/time from Dos format to tm_unz (readable more easilty) + */ +local void +unzlocal_DosDateToTmuDate(uLong ulDosDate, tm_unz *ptm) +{ + uLong uDate; + + uDate = (uLong) (ulDosDate >> 16); + ptm->tm_mday = (uInt) (uDate & 0x1f); + ptm->tm_mon = (uInt) ((((uDate) & 0x1E0) / 0x20) - 1); + ptm->tm_year = (uInt) (((uDate & 0x0FE00) / 0x0200) + 1980); + + ptm->tm_hour = (uInt) ((ulDosDate & 0xF800) / 0x800); + ptm->tm_min = (uInt) ((ulDosDate & 0x7E0) / 0x20); + ptm->tm_sec = (uInt) (2 * (ulDosDate & 0x1f)); +} + +/* + * Get Info about the current file in the zipfile, with internal only info + */ +local int unzlocal_GetCurrentFileInfoInternal +OF((unzFile file, + unz_file_info * pfile_info, + unz_file_info_internal + * pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + + local int unzlocal_GetCurrentFileInfoInternal(file, + pfile_info , + pfile_info_internal, + szFileName , fileNameBufferSize, + extraField , extraFieldBufferSize, + szComment , commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s *s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err = UNZ_OK; + uLong uMagic; + long lSeek = 0; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir + s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + + /* we check the magic */ + if (err == UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &uMagic) != UNZ_OK) + err = UNZ_ERRNO; + else if (uMagic != 0x02014b50) + err = UNZ_BADZIPFILE; + } + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &file_info.version) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &file_info.version_needed) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &file_info.flag) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &file_info.compression_method) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &file_info.dosDate) != UNZ_OK) + err = UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate, &file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &file_info.crc) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &file_info.compressed_size) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &file_info.uncompressed_size) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &file_info.size_filename) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &file_info.size_file_extra) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &file_info.size_file_comment) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &file_info.disk_num_start) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &file_info.internal_fa) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &file_info.external_fa) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &file_info_internal.offset_curfile) != UNZ_OK) + err = UNZ_ERRNO; + + lSeek += file_info.size_filename; + if ((err == UNZ_OK) && (szFileName != NULL)) { + uLong uSizeRead; + + if (file_info.size_filename < fileNameBufferSize) { + *(szFileName + file_info.size_filename) = '\0'; + uSizeRead = file_info.size_filename; + } else + uSizeRead = fileNameBufferSize; + + if ((file_info.size_filename > 0) && (fileNameBufferSize > 0)) + if (ZREAD(s->z_filefunc, s->filestream, szFileName, uSizeRead) != uSizeRead) + err = UNZ_ERRNO; + lSeek -= uSizeRead; + } + if ((err == UNZ_OK) && (extraField != NULL)) { + uLong uSizeRead; + + if (file_info.size_file_extra < extraFieldBufferSize) + uSizeRead = file_info.size_file_extra; + else + uSizeRead = extraFieldBufferSize; + + if (lSeek != 0) { + if (ZSEEK(s->z_filefunc, s->filestream, lSeek, ZLIB_FILEFUNC_SEEK_CUR) == 0) + lSeek = 0; + else + err = UNZ_ERRNO; + } + if ((file_info.size_file_extra > 0) && (extraFieldBufferSize > 0)) + if (ZREAD(s->z_filefunc, s->filestream, extraField, uSizeRead) != uSizeRead) + err = UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } else + lSeek += file_info.size_file_extra; + + + if ((err == UNZ_OK) && (szComment != NULL)) { + uLong uSizeRead; + + if (file_info.size_file_comment < commentBufferSize) { + *(szComment + file_info.size_file_comment) = '\0'; + uSizeRead = file_info.size_file_comment; + } else + uSizeRead = commentBufferSize; + + if (lSeek != 0) { + if (ZSEEK(s->z_filefunc, s->filestream, lSeek, ZLIB_FILEFUNC_SEEK_CUR) == 0) + lSeek = 0; + else + err = UNZ_ERRNO; + } + if ((file_info.size_file_comment > 0) && (commentBufferSize > 0)) + if (ZREAD(s->z_filefunc, s->filestream, szComment, uSizeRead) != uSizeRead) + err = UNZ_ERRNO; + lSeek += file_info.size_file_comment - uSizeRead; + } else + lSeek += file_info.size_file_comment; + + if ((err == UNZ_OK) && (pfile_info != NULL)) + *pfile_info = file_info; + + if ((err == UNZ_OK) && (pfile_info_internal != NULL)) + *pfile_info_internal = file_info_internal; + + return err; +} + + + +/* + * Write info about the ZipFile in the *pglobal_info structure. No + * preparation of the structure is needed return UNZ_OK if there is no + * problem. + */ +extern int ZEXPORT +unzGetCurrentFileInfo(file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file, pfile_info, NULL, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize); +} + +/* + * Set the current file of the zipfile to the first file. return UNZ_OK if + * there is no problem + */ +extern int ZEXPORT +unzGoToFirstFile(file) + unzFile file; +{ + int err = UNZ_OK; + unz_s *s; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + s->pos_in_central_dir = s->offset_central_dir; + s->num_file = 0; + err = unzlocal_GetCurrentFileInfoInternal(file, &s->cur_file_info, + &s->cur_file_info_internal, + NULL, 0, NULL, 0, NULL, 0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + * Set the current file of the zipfile to the next file. return UNZ_OK if + * there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was + * the latest. + */ +extern int ZEXPORT +unzGoToNextFile(file) + unzFile file; +{ + unz_s *s; + int err; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file + 1 == s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file, &s->cur_file_info, + &s->cur_file_info_internal, + NULL, 0, NULL, 0, NULL, 0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + * Try locate the file szFileName in the zipfile. For the iCaseSensitivity + * signification, see unzipStringFileNameCompare + * + * return value : UNZ_OK if the file is found. It becomes the current file. + * UNZ_END_OF_LIST_OF_FILE if the file is not found + */ +extern int ZEXPORT +unzLocateFile(file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s *s; + int err; + + /* + * We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file == NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName) >= UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s = (unz_s *) file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP + 1]; + + err = unzGetCurrentFileInfo(file, NULL, + szCurrentFileName, sizeof(szCurrentFileName) - 1, + NULL, 0, NULL, 0); + if (err == UNZ_OK) { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName, iCaseSensitivity) == 0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* + * We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved; + s->pos_in_central_dir = pos_in_central_dirSaved; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* + * / + */ ////////////////////////////////////////*/ +/* Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) */ +/* I need random access */ +// +/* Further optimization could be realized by adding an ability */ +/* to cache the directory in memory. The goal being a single */ +/* comprehensive file read to put the file I need in a memory. */ + +/* + * typedef struct unz_file_pos_s { uLong pos_in_zip_directory; offset in + * file + */ +#if 0 +uLong num_of_file; /* # of file */ +} unz_file_pos; +#endif + +extern int ZEXPORT +unzGetFilePos(file, file_pos) + unzFile file; + unz_file_pos *file_pos; +{ + unz_s *s; + + if (file == NULL || file_pos == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT +unzGoToFilePos(file, file_pos) + unzFile file; + unz_file_pos *file_pos; +{ + unz_s *s; + int err; + + if (file == NULL || file_pos == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file, &s->cur_file_info, + &s->cur_file_info_internal, + NULL, 0, NULL, 0, NULL, 0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + * Unzip Helper Functions - should be here? + */ +/* ///////////////////////////////////////// */ + +/* + * Read the local header of the current zipfile Check the coherency of the + * local header and info in the end of central directory about this file + * store in *piSizeVar the size of extra info in local header (filename and + * size of extra field data) + */ +local int +unzlocal_CheckCurrentFileCoherencyHeader(unz_s *s, uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic , uData, uFlags; + uLong size_filename; + uLong size_extra_field; + int err = UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream, s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + + if (err == UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &uMagic) != UNZ_OK) + err = UNZ_ERRNO; + else if (uMagic != 0x04034b50) + err = UNZ_BADZIPFILE; + } + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &uData) != UNZ_OK) + err = UNZ_ERRNO; + + /* + * else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + * err=UNZ_BADZIPFILE; + */ + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &uFlags) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &uData) != UNZ_OK) + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (uData != s->cur_file_info.compression_method)) + err = UNZ_BADZIPFILE; + + if ((err == UNZ_OK) && (s->cur_file_info.compression_method != 0) && + (s->cur_file_info.compression_method != Z_DEFLATED)) + err = UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &uData) != UNZ_OK) /* date/time */ + err = UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &uData) != UNZ_OK) /* crc */ + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (uData != s->cur_file_info.crc) && + ((uFlags & 8) == 0)) + err = UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &uData) != UNZ_OK) /* size compr */ + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (uData != s->cur_file_info.compressed_size) && + ((uFlags & 8) == 0)) + err = UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream, &uData) != UNZ_OK) /* size uncompr */ + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (uData != s->cur_file_info.uncompressed_size) && + ((uFlags & 8) == 0)) + err = UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &size_filename) != UNZ_OK) + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (size_filename != s->cur_file_info.size_filename)) + err = UNZ_BADZIPFILE; + + *piSizeVar += (uInt) size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream, &size_extra_field) != UNZ_OK) + err = UNZ_ERRNO; + *poffset_local_extrafield = s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt) size_extra_field; + + *piSizeVar += (uInt) size_extra_field; + + return err; +} + +/* + * Open for reading data the current file in the zipfile. If there is no + * error and the file is opened, the return value is UNZ_OK. + */ +extern int ZEXPORT +unzOpenCurrentFile3(file, method, level, raw, password) + unzFile file; + int *method; + int *level; + int raw; + const char *password; +{ + int err = UNZ_OK; + uInt iSizeVar; + unz_s *s; + file_in_zip_read_info_s *pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local + * extra field */ + uInt size_local_extrafield; /* size of the local extra + * field */ + +#ifndef NOUNCRYPT + char source [12]; + +#else + if (password != NULL) + return UNZ_PARAMERROR; +#endif + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s, &iSizeVar, + &offset_local_extrafield, &size_local_extrafield) != UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s *) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info == NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer = (char *)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield = 0; + pfile_in_zip_read_info->raw = raw; + + if (pfile_in_zip_read_info->read_buffer == NULL) { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + pfile_in_zip_read_info->stream_initialised = 0; + + if (method != NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level != NULL) { + *level = 6; + switch (s->cur_file_info.flag & 0x06) { + case 6: + *level = 1; + break; + case 4: + *level = 2; + break; + case 2: + *level = 9; + break; + } + } + if ((s->cur_file_info.compression_method != 0) && + (s->cur_file_info.compression_method != Z_DEFLATED)) + err = UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait = s->cur_file_info.crc; + pfile_in_zip_read_info->crc32 = 0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream = s->filestream; + pfile_in_zip_read_info->z_filefunc = s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile = s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method == Z_DEFLATED) && + (!raw)) { + pfile_in_zip_read_info->stream.zalloc = (alloc_func) 0; + pfile_in_zip_read_info->stream.zfree = (free_func) 0; + pfile_in_zip_read_info->stream.opaque = (voidpf) 0; + pfile_in_zip_read_info->stream.next_in = (voidpf) 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err = inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised = 1; + else { + TRYFREE(pfile_in_zip_read_info); + return err; + } + + /* + * windowBits is passed < 0 to tell that there is no zlib + * header. Note that in this case inflate *requires* an extra + * "dummy" byte after the compressed stream in order to + * complete decompression and return Z_STREAM_END. In unzip, + * i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt) 0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +#ifndef NOUNCRYPT + if (password != NULL) { + int i; + + s->pcrc_32_tab = get_crc_table(); + init_keys(password, s->keys, s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET) != 0) + return UNZ_INTERNALERROR; + if (ZREAD(s->z_filefunc, s->filestream, source, 12) < 12) + return UNZ_INTERNALERROR; + + for (i = 0; i < 12; i++) + zdecode(s->keys, s->pcrc_32_tab, source[i]); + + s->pfile_in_zip_read->pos_in_zipfile += 12; + s->encrypted = 1; + } +#endif + + + return UNZ_OK; +} + +extern int ZEXPORT +unzOpenCurrentFile(file) + unzFile file; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT +unzOpenCurrentFilePassword(file, password) + unzFile file; + const char *password; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT +unzOpenCurrentFile2(file, method, level, raw) + unzFile file; + int *method; + int *level; + int raw; +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* + * Read bytes from the current file. buf contain buffer where data must be + * copied len the size of buf. + * + * return the number of byte copied if somes bytes are copied return 0 if the + * end of file was reached return <0 with error code if there is an error + * (UNZ_ERRNO for IO error, or zLib error for uncompress error) + */ +extern int ZEXPORT +unzReadCurrentFile(file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err = UNZ_OK; + uInt iRead = 0; + unz_s *s; + file_in_zip_read_info_s *pfile_in_zip_read_info; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len == 0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef *) buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt) len; + + if ((len > pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt) pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len > pfile_in_zip_read_info->rest_read_compressed + + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt) pfile_in_zip_read_info->rest_read_compressed + + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out > 0) { + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed > 0)) { + uInt uReadThis = UNZ_BUFSIZE; + + if (pfile_in_zip_read_info->rest_read_compressed < uReadThis) + uReadThis = (uInt) pfile_in_zip_read_info->rest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis) != uReadThis) + return UNZ_ERRNO; + + +#ifndef NOUNCRYPT + if (s->encrypted) { + uInt i; + + for (i = 0; i < uReadThis; i++) + pfile_in_zip_read_info->read_buffer[i] = + zdecode(s->keys, s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +#endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed -= uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef *) pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt) uReadThis; + } + if ((pfile_in_zip_read_info->compression_method == 0) || (pfile_in_zip_read_info->raw)) { + uInt uDoCopy , i; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead == 0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in; + + for (i = 0; i < uDoCopy; i++) + *(pfile_in_zip_read_info->stream.next_out + i) = + *(pfile_in_zip_read_info->stream.next_in + i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed -= uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } else { + uLong uTotalOutBefore, uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush = Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + * if + * ((pfile_in_zip_read_info->rest_read_uncompressed + * == pfile_in_zip_read_info->stream.avail_out) && + * (pfile_in_zip_read_info->rest_read_compressed == + * 0)) flush = Z_FINISH; + */ + err = inflate(&pfile_in_zip_read_info->stream, flush); + + if ((err >= 0) && (pfile_in_zip_read_info->stream.msg != NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter - uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32, bufBefore, + (uInt) (uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt) (uTotalOutAfter - uTotalOutBefore); + + if (err == Z_STREAM_END) + return (iRead == 0) ? UNZ_EOF : iRead; + if (err != Z_OK) + break; + } + } + + if (err == Z_OK) + return iRead; + return err; +} + + +/* + * Give the current position in uncompressed data + */ +extern z_off_t ZEXPORT +unztell(file) + unzFile file; +{ + unz_s *s; + file_in_zip_read_info_s *pfile_in_zip_read_info; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + return (z_off_t) pfile_in_zip_read_info->stream.total_out; +} + + +/* + * return 1 if the end of file was reached, 0 elsewhere + */ +extern int ZEXPORT +unzeof(file) + unzFile file; +{ + unz_s *s; + file_in_zip_read_info_s *pfile_in_zip_read_info; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + * Read extra field from the current file (opened by unzOpenCurrentFile) This + * is the local-header version of the extra field (sometimes, there is more + * info in the local-header version than in the central-header) + * + * if buf==NULL, it return the size of the local extra field that can be read + * + * if buf!=NULL, len is the size of the buffer, the extra header is copied in + * buf. the return value is the number of bytes copied in buf, or (if <0) the + * error code + */ +extern int ZEXPORT +unzGetLocalExtrafield(file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s *s; + file_in_zip_read_info_s *pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf == NULL) + return (int)size_to_read; + + if (len > size_to_read) + read_now = (uInt) size_to_read; + else + read_now = (uInt) len; + + if (read_now == 0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf, read_now) != read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + * Close the file in zip opened with unzipOpenCurrentFile Return UNZ_CRCERROR + * if all the file was read but the CRC is not good + */ +extern int ZEXPORT +unzCloseCurrentFile(file) + unzFile file; +{ + int err = UNZ_OK; + + unz_s *s; + file_in_zip_read_info_s *pfile_in_zip_read_info; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err = UNZ_CRCERROR; + } + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read = NULL; + + return err; +} + + +/* + * Get the global comment string of the ZipFile, in the szComment buffer. + * uSizeBuf is the size of the szComment buffer. return the number of byte + * copied or an error code <0 + */ +extern int ZEXPORT +unzGetGlobalComment(file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + /* int err=UNZ_OK; */ + unz_s *s; + uLong uReadThis; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + + uReadThis = uSizeBuf; + if (uReadThis > s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc, s->filestream, s->central_pos + 22, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + if (uReadThis > 0) { + *szComment = '\0'; + if (ZREAD(s->z_filefunc, s->filestream, szComment, uReadThis) != uReadThis) + return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment + s->gi.size_comment) = '\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong ZEXPORT +unzGetOffset(file) + unzFile file; +{ + unz_s *s; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file == s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT +unzSetOffset(file, pos) + unzFile file; + uLong pos; +{ + unz_s *s; + int err; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *) file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file, &s->cur_file_info, + &s->cur_file_info_internal, + NULL, 0, NULL, 0, NULL, 0); + s->current_file_ok = (err == UNZ_OK); + return err; +} diff --git a/src/unzip/unzip.h b/src/unzip/unzip.h new file mode 100644 index 00000000..66be49ab --- /dev/null +++ b/src/unzip/unzip.h @@ -0,0 +1,398 @@ +/* + * unzip.h -- IO for uncompress .zip files using zlib Version 1.01e, February + * 12th, 2005 + * + * Copyright (C) 1998-2005 Gilles Vollant + * + * This unzip package allow extract file from .ZIP file, compatible with PKZip + * 2.04g WinZip, InfoZip tools and compatible. + * + * Multi volume ZipFile (span) are not supported. Encryption compatible with + * pkzip 2.04g only supported Old compressions used by old PKZip 1.x are not + * supported + * + * + * I WAIT FEEDBACK at mail info@winimage.com Visit also + * http://www.winimage.com/zLibDll/unzip.htm for evolution + */ +/* +Condition of use and distribution are the same than zlib: + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, including commercial applications, + and to alter it and redistribute it + freely , subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software.If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* + * for more info about .ZIP format, see + * http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + */ +/* + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at: + ftp://ftp.pkware.com/probdesc.zip + */ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) + + /* + * like the STRICT of WIN32, we define a pointer that cannot be + * converted from (void*) without cast + */ + typedef struct TagunzFile__ { + int unused; + } unzFile__; + typedef unzFile__ *unzFile; +#else + typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + /* tm_unz contain date/time info */ + typedef struct tm_unz_s { + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - + * [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ + } tm_unz; + + /* + * unz_global_info structure contain global data about the ZIPfile + * These data comes from the end of central dir + */ + typedef struct unz_global_info_s { + uLong number_entry; /* total number of entries in + * the central dir on this + * disk */ + uLong size_comment; /* size of the global comment + * of the zipfile */ + } unz_global_info; + + + /* unz_file_info contain information about a file in the zipfile */ + typedef struct unz_file_info_s { + uLong version; /* version made by + * 2 bytes */ + uLong version_needed; /* version needed to extract + * 2 bytes */ + uLong flag; /* general purpose bit flag 2 + * bytes */ + uLong compression_method; /* compression method + * 2 bytes */ + uLong dosDate; /* last mod file date in Dos + * fmt 4 bytes */ + uLong crc; /* crc-32 4 + * bytes */ + uLong compressed_size; /* compressed size + * 4 bytes */ + uLong uncompressed_size; /* uncompressed size + * 4 bytes */ + uLong size_filename; /* filename length + * 2 bytes */ + uLong size_file_extra; /* extra field length + * 2 bytes */ + uLong size_file_comment; /* file comment length + * 2 bytes */ + + uLong disk_num_start; /* disk number start + * 2 bytes */ + uLong internal_fa; /* internal file attributes + * 2 bytes */ + uLong external_fa; /* external file attributes + * 4 bytes */ + + tm_unz tmu_date; + } unz_file_info; + + extern int ZEXPORT unzStringFileNameCompare OF((const char *fileName1, + const char *fileName2, + int iCaseSensitivity)); + + /* + * Compare two filename (fileName1,fileName2). If iCaseSenisivity = + * 1, comparision is case sensitivity (like strcmp) If + * iCaseSenisivity = 2, comparision is not case sensitivity (like + * strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is + * defaut of your operating system (like 1 on Unix, 2 on Windows) + */ + + + extern unzFile ZEXPORT unzOpen OF((const char *path)); + + /* + * Open a Zip file. path contain the full pathname (by example, on a + * Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + * "zlib/zlib113.zip". If the zipfile cannot be opened (file don't + * exist or in not valid), the return value is NULL. Else, the return + * value is a unzFile Handle, usable with other function of this + * unzip package. + */ + + extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def * pzlib_filefunc_def)); + + /* + * Open a Zip file, like unzOpen, but provide a set of file low level + * API for read/write the zip file (see ioapi.h) + */ + + extern int ZEXPORT unzClose OF((unzFile file)); + + /* + * Close a ZipFile opened with unzipOpen. If there is files inside + * the .Zip opened with unzOpenCurrentFile (see later), these files + * MUST be closed with unzipCloseCurrentFile before call unzipClose. + * return UNZ_OK if there is no problem. + */ + + extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info * pglobal_info)); + + /* + * Write info about the ZipFile in the *pglobal_info structure. No + * preparation of the structure is needed return UNZ_OK if there is + * no problem. + */ + + + extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); + + /* + * Get the global comment string of the ZipFile, in the szComment + * buffer. uSizeBuf is the size of the szComment buffer. return the + * number of byte copied or an error code <0 + */ + + + /***************************************************************************/ + /* Unzip package allow you browse the directory of the zipfile */ + + extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); + + /* + * Set the current file of the zipfile to the first file. return + * UNZ_OK if there is no problem + */ + + extern int ZEXPORT unzGoToNextFile OF((unzFile file)); + + /* + * Set the current file of the zipfile to the next file. return + * UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if + * the actual file was the latest. + */ + + extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); + + /* + * Try locate the file szFileName in the zipfile. For the + * iCaseSensitivity signification, see unzStringFileNameCompare + * + * return value : UNZ_OK if the file is found. It becomes the current + * file. UNZ_END_OF_LIST_OF_FILE if the file is not found + */ + + + /* ****************************************** */ + /* Ryan supplied functions */ + /* unz_file_info contain information about a file in the zipfile */ + typedef struct unz_file_pos_s { + uLong pos_in_zip_directory; /* offset in zip file + * directory */ + uLong num_of_file; /* # of file */ + } unz_file_pos; + + extern int ZEXPORT unzGetFilePos( + unzFile file , + unz_file_pos * file_pos); + + extern int ZEXPORT unzGoToFilePos( + unzFile file , + unz_file_pos * file_pos); + + /* ****************************************** */ + + extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info * pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + + /* + * Get Info about the current file if pfile_info!=NULL, the + * *pfile_info structure will contain somes info about the current + * file if szFileName!=NULL, the filemane string will be copied in + * szFileName (fileNameBufferSize is the size of the buffer) if + * extraField!=NULL, the extra field information will be copied in + * extraField (extraFieldBufferSize is the size of the buffer). This + * is the Central-header version of the extra field if + * szComment!=NULL, the comment string of the file will be copied in + * szComment (commentBufferSize is the size of the buffer) + */ + + /***************************************************************************/ + + /* + * for reading the content of the current zipfile, you can open it, + * read data from it, and close it (you can close it before reading + * all the file) + */ + + extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); + + /* + * Open for reading data the current file in the zipfile. If there is + * no error, the return value is UNZ_OK. + */ + + extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char *password)); + + /* + * Open for reading data the current file in the zipfile. password is + * a crypting password If there is no error, the return value is + * UNZ_OK. + */ + + extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int *method, + int *level, + int raw)); + + /* + * Same than unzOpenCurrentFile, but open for read raw the file (not + * uncompress) if raw==1 method will receive method of compression, + * *level will receive level of compression note : you can set level + * parameter as NULL (if you did not want known level, but you CANNOT + * set method parameter as NULL + */ + + extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int *method, + int *level, + int raw , + const char *password)); + + /* + * Same than unzOpenCurrentFile, but open for read raw the file (not + * uncompress) if raw==1 method will receive method of compression, + * *level will receive level of compression note : you can set level + * parameter as NULL (if you did not want known level, but you CANNOT + * set method parameter as NULL + */ + + + extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); + + /* + * Close the file in zip opened with unzOpenCurrentFile Return + * UNZ_CRCERROR if all the file was read but the CRC is not good + */ + + extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf , + unsigned len)); + + /* + * Read bytes from the current file (opened by unzOpenCurrentFile) + * buf contain buffer where data must be copied len the size of buf. + * + * return the number of byte copied if somes bytes are copied return 0 + * if the end of file was reached return <0 with error code if there + * is an error (UNZ_ERRNO for IO error, or zLib error for uncompress + * error) + */ + + extern z_off_t ZEXPORT unztell OF((unzFile file)); + + /* + * Give the current position in uncompressed data + */ + + extern int ZEXPORT unzeof OF((unzFile file)); + + /* + * return 1 if the end of file was reached, 0 elsewhere + */ + + extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf , + unsigned len)); + + /* + * Read extra field from the current file (opened by + * unzOpenCurrentFile) This is the local-header version of the extra + * field (sometimes, there is more info in the local-header version + * than in the central-header) + * + * if buf==NULL, it return the size of the local extra field + * + * if buf!=NULL, len is the size of the buffer, the extra header is + * copied in buf. the return value is the number of bytes copied in + * buf, or (if <0) the error code + */ + + /***************************************************************************/ + + /* Get the current file offset */ + extern uLong ZEXPORT unzGetOffset(unzFile file); + + /* Set the current file offset */ + extern int ZEXPORT unzSetOffset(unzFile file, uLong pos); + + + +#ifdef __cplusplus +} + +#endif + +#endif /* _unz_H */