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