1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10  *
11  * See the GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16  *
17  *
18  */
19 
20 #include "qwsvdef.h"
21 
22 //#define	MAX_FILES_IN_PACK	2048
23 
24 // on disk
25 typedef struct
26 {
27 	char    name[56];
28 	int     filepos, filelen;
29 } dpackfile_t;
30 
31 // on disk
32 typedef struct
33 {
34 	char    id[4];
35 	int     dirofs;
36 	int     dirlen;
37 } dpackheader_t;
38 
39 // Packages in memory
40 typedef struct packfile_s
41 {
42 	char name[MAX_QPATH];
43 	int filepos, filelen;
44 } packfile_t;
45 
46 typedef struct pack_s
47 {
48 	char    filename[MAX_OSPATH];
49 	vfsfile_t    *handle;
50 	unsigned int filepos;   // the pos the subfiles left it at
51 							// (to optimize calls to vfs_seek)
52 	int references;         // seeing as all vfiles from a pak file use the
53 							// parent's vfsfile, we need to keep the parent
54 							// open until all subfiles are closed.
55 
56 	int     numfiles;
57 	packfile_t  *files;
58 } pack_t;
59 
60 typedef struct {
61     vfsfile_t funcs; // <= must be at top/begining of struct
62 	pack_t *parentpak;
63     unsigned long startpos;
64     unsigned long length;
65     unsigned long currentpos;
66 } vfspack_t;
67 
68 
69 //=====================================
70 //PACK files (*.pak) - VFS functions
71 //=====================================
VFSPAK_ReadBytes(struct vfsfile_s * vfs,void * buffer,int bytestoread,vfserrno_t * err)72 static int VFSPAK_ReadBytes (struct vfsfile_s *vfs, void *buffer, int bytestoread, vfserrno_t *err)
73 {
74 	vfspack_t *vfsp = (vfspack_t*)vfs;
75 	int read;
76 
77 	if (vfsp->currentpos - vfsp->startpos + bytestoread > vfsp->length)
78 		bytestoread = vfsp->length - (vfsp->currentpos - vfsp->startpos);
79 	if (bytestoread <= 0)
80 		return -1;
81 
82 	if (vfsp->parentpak->filepos != vfsp->currentpos) {
83 		VFS_SEEK(vfsp->parentpak->handle, vfsp->currentpos, SEEK_SET);
84 	}
85 
86 	read = VFS_READ(vfsp->parentpak->handle, buffer, bytestoread, err);
87 	vfsp->currentpos += read;
88 	vfsp->parentpak->filepos = vfsp->currentpos;
89 
90 	// VFS-FIXME: Need to handle errors
91 
92 	return read;
93 }
VFSPAK_WriteBytes(struct vfsfile_s * vfs,const void * buffer,int bytestoread)94 static int VFSPAK_WriteBytes (struct vfsfile_s *vfs, const void *buffer, int bytestoread)
95 {	//not supported.
96 	Sys_Error("VFSPAK_WriteBytes: Cannot write to pak files");
97 	return 0;
98 }
99 
VFSPAK_Seek(struct vfsfile_s * vfs,unsigned long offset,int whence)100 static int VFSPAK_Seek (struct vfsfile_s *vfs, unsigned long offset, int whence)
101 {
102 	vfspack_t *vfsp = (vfspack_t*)vfs;
103 
104 	// VFS-FIXME Support other whence types
105 	switch(whence) {
106 	case SEEK_SET:
107 		vfsp->currentpos = vfsp->startpos + offset;
108 		break;
109 	case SEEK_CUR:
110 		vfsp->currentpos += offset;
111 		break;
112 	case SEEK_END:
113 		vfsp->currentpos = vfsp->startpos + vfsp->length + offset;
114 		break;
115 	default:
116 		Sys_Error("VFSTAR_Seek: Unknown whence value(%d)\n", whence);
117 		return -1;
118 	}
119 
120 	if (vfsp->currentpos > vfsp->length) {
121 		Con_Printf("VFSPAK_Seek: Warning seeking past the file's size\n");
122 	}
123 
124 	return 0;
125 }
126 
VFSPAK_Tell(struct vfsfile_s * vfs)127 static unsigned long VFSPAK_Tell (struct vfsfile_s *vfs)
128 {
129 	vfspack_t *vfsp = (vfspack_t*)vfs;
130 	return vfsp->currentpos - vfsp->startpos;
131 }
132 
VFSPAK_GetLen(struct vfsfile_s * vfs)133 static unsigned long VFSPAK_GetLen (struct vfsfile_s *vfs)
134 {
135 	vfspack_t *vfsp = (vfspack_t*)vfs;
136 	return vfsp->length;
137 }
138 
139 static void FSPAK_ClosePath(void *handle);
VFSPAK_Close(vfsfile_t * vfs)140 static void VFSPAK_Close(vfsfile_t *vfs)
141 {
142 	vfspack_t *vfsp = (vfspack_t*)vfs;
143 	FSPAK_ClosePath(vfsp->parentpak);	// tell the parent that we don't need it open any
144 										// more (reference counts)
145 	Q_free(vfsp);
146 }
147 
FSPAK_OpenVFS(void * handle,flocation_t * loc,char * mode)148 static vfsfile_t *FSPAK_OpenVFS(void *handle, flocation_t *loc, char *mode)
149 {
150 	pack_t *pack = (pack_t*)handle;
151 	vfspack_t *vfsp;
152 
153 	if (strcmp(mode, "rb"))
154 		return NULL; //urm, unable to write/append
155 
156 	vfsp = Q_calloc(1, sizeof(*vfsp));
157 
158 	vfsp->parentpak = pack;
159 	vfsp->parentpak->references++;
160 
161 	vfsp->startpos   = loc->offset;
162 	vfsp->length     = loc->len;
163 	vfsp->currentpos = vfsp->startpos;
164 
165 	vfsp->funcs.ReadBytes     = strcmp(mode, "rb") ? NULL : VFSPAK_ReadBytes;
166 	vfsp->funcs.WriteBytes    = strcmp(mode, "wb") ? NULL : VFSPAK_WriteBytes;
167 	vfsp->funcs.Seek		  = VFSPAK_Seek;
168 	vfsp->funcs.Tell		  = VFSPAK_Tell;
169 	vfsp->funcs.GetLen	      = VFSPAK_GetLen;
170 	vfsp->funcs.Close	      = VFSPAK_Close;
171 	vfsp->funcs.Flush         = NULL;
172 	if (loc->search)
173 		vfsp->funcs.copyprotected = loc->search->copyprotected;
174 
175 	return (vfsfile_t *)vfsp;
176 }
177 
178 //======================================
179 // PACK files (*.pak) - Search functions
180 //======================================
FSPAK_PrintPath(void * handle)181 static void FSPAK_PrintPath(void *handle)
182 {
183 	pack_t *pak = handle;
184 
185 	if (pak->references != 1)
186 		Con_Printf("%s (%i)\n", pak->filename, pak->references-1);
187 	else
188 		Con_Printf("%s\n", pak->filename);
189 }
190 
FSPAK_ClosePath(void * handle)191 static void FSPAK_ClosePath(void *handle)
192 {
193 	pack_t *pak = handle;
194 
195 	pak->references--;
196 	if (pak->references > 0)
197 		return;	//not free yet
198 
199 	VFS_CLOSE (pak->handle);
200 	if (pak->files)
201 		Q_free(pak->files);
202 	Q_free(pak);
203 }
204 
FSPAK_BuildHash(void * handle)205 static void FSPAK_BuildHash(void *handle)
206 {
207 	pack_t *pak = handle;
208 	int i;
209 
210 	for (i = 0; i < pak->numfiles; i++)
211 	{
212 		if (!Hash_GetInsensitive(filesystemhash, pak->files[i].name))
213 		{
214 			Hash_AddInsensitive(filesystemhash, pak->files[i].name, &pak->files[i]);
215 			fs_hash_files++;
216 		}
217 		else
218 			fs_hash_dups++;
219 	}
220 }
221 
FSPAK_FLocate(void * handle,flocation_t * loc,const char * filename,void * hashedresult)222 static qbool FSPAK_FLocate(void *handle, flocation_t *loc, const char *filename, void *hashedresult)
223 {
224 	packfile_t *pf = hashedresult;
225 	int i;
226 	pack_t		*pak = handle;
227 
228 // look through all the pak file elements
229 
230 	if (pf)
231 	{	//is this a pointer to a file in this pak?
232 		if (pf < pak->files || pf > pak->files + pak->numfiles)
233 			return false;	//was found in a different path
234 	}
235 	else
236 	{
237 		for (i=0 ; i<pak->numfiles ; i++)	//look for the file
238 		{
239 			if (!strcmp (pak->files[i].name, filename))
240 			{
241 				pf = &pak->files[i];
242 				break;
243 			}
244 		}
245 	}
246 
247 	if (pf)
248 	{
249 		if (loc)
250 		{
251 			loc->index = pf - pak->files;
252 			snprintf(loc->rawname, sizeof(loc->rawname), "%s/%s", pak->filename, filename);
253 			loc->offset = pf->filepos;
254 			loc->len = pf->filelen;
255 		}
256 		return true;
257 	}
258 	return false;
259 }
260 
FSPAK_EnumerateFiles(void * handle,char * match,int (* func)(char *,int,void *),void * parm)261 static int FSPAK_EnumerateFiles (void *handle, char *match, int (*func)(char *, int, void *), void *parm)
262 {
263 	pack_t	*pak = handle;
264 	int		num;
265 
266 	for (num = 0; num<(int)pak->numfiles; num++)
267 	{
268 		if (wildcmp(match, pak->files[num].name))
269 		{
270 			if (!func(pak->files[num].name, pak->files[num].filelen, parm))
271 				return false;
272 		}
273 	}
274 
275 	return true;
276 }
277 
278 /*
279 =================
280 FSPAK_LoadPackFile
281 
282 Takes an explicit (not game tree related) path to a pak file.
283 
284 Loads the header and directory, adding the files at the beginning
285 of the list so they override previous pack files.
286 =================
287 */
FSPAK_LoadPackFile(vfsfile_t * file,const char * desc)288 static void *FSPAK_LoadPackFile (vfsfile_t *file, const char *desc)
289 {
290 	dpackheader_t	header;
291 	int				i;
292 	packfile_t		*newfiles;
293 	int				numpackfiles;
294 	pack_t			*pack;
295 	vfsfile_t		*packhandle;
296 	dpackfile_t		info;
297 	int read;
298 	vfserrno_t err;
299 
300 	packhandle = file;
301 	if (packhandle == NULL)
302 		return NULL;
303 
304 	VFS_READ(packhandle, &header, sizeof(header), &err);
305 	if (header.id[0] != 'P' || header.id[1] != 'A'
306 	|| header.id[2] != 'C' || header.id[3] != 'K')
307 	{
308 		return NULL;
309 	}
310 	header.dirofs = LittleLong (header.dirofs);
311 	header.dirlen = LittleLong (header.dirlen);
312 
313 	numpackfiles = header.dirlen / sizeof(dpackfile_t);
314 
315 //	if (numpackfiles != PAK0_COUNT)
316 //		com_modified = true;	// not the original file
317 
318 	newfiles = (packfile_t*)Q_malloc (numpackfiles * sizeof(packfile_t));
319 
320 	VFS_SEEK(packhandle, header.dirofs, SEEK_SET);
321 //	fread (&info, 1, header.dirlen, packhandle);
322 
323 // crc the directory to check for modifications
324 //	crc = QCRC_Block((qbyte *)info, header.dirlen);
325 
326 
327 //	QCRC_Init (&crc);
328 
329 	pack = (pack_t *)Q_calloc(1, sizeof (pack_t));
330 
331 // parse the directory
332 	for (i=0 ; i<numpackfiles ; i++)
333 	{
334 		*info.name = '\0';
335 		read = VFS_READ(packhandle, &info, sizeof(info), &err);
336 /*
337 		for (j=0 ; j<sizeof(info) ; j++)
338 			CRC_ProcessByte(&crc, ((qbyte *)&info)[j]);
339 */
340 		strlcpy (newfiles[i].name, info.name, MAX_QPATH);
341 		newfiles[i].filepos = LittleLong(info.filepos);
342 		newfiles[i].filelen = LittleLong(info.filelen);
343 	}
344 /*
345 	if (crc != PAK0_CRC)
346 		com_modified = true;
347 */
348 	strlcpy (pack->filename, desc, sizeof (pack->filename));
349 	pack->handle = packhandle;
350 	pack->numfiles = numpackfiles;
351 	pack->files = newfiles;
352 	pack->filepos = 0;
353 	VFS_SEEK(packhandle, pack->filepos, SEEK_SET);
354 
355 	pack->references++;
356 
357 	return pack;
358 }
359 
360 extern void FSOS_ReadFile(void *handle, flocation_t *loc, char *buffer);
361 
362 searchpathfuncs_t packfilefuncs = {
363 	FSPAK_PrintPath,
364 	FSPAK_ClosePath,
365 	FSPAK_BuildHash,
366 	FSPAK_FLocate,
367 	FSOS_ReadFile,
368 	FSPAK_EnumerateFiles,
369 	FSPAK_LoadPackFile,
370 	NULL,
371 	FSPAK_OpenVFS
372 };
373