1 /*
2  * quakefs.c -- Hexen II filesystem
3  * $Id: quakefs.c 5787 2017-01-03 22:22:34Z sezero $
4  *
5  * Copyright (C) 1996-1997  Id Software, Inc.
6  * Copyright (C) 2005-2012  O.Sezer <sezero@users.sourceforge.net>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or (at
11  * your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  * See the GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23 
24 #include "quakedef.h"
25 #include "pakfile.h"
26 #include <errno.h>
27 #ifdef PLATFORM_WINDOWS
28 #include <io.h>
29 #endif
30 #include "filenames.h"
31 
32 typedef struct
33 {
34 	char	name[MAX_QPATH];
35 	int	filepos, filelen;
36 } pakfiles_t;
37 
38 typedef struct pack_s
39 {
40 	char	filename[MAX_OSPATH];
41 	FILE	*handle;
42 	int	numfiles;
43 	pakfiles_t	*files;
44 } pack_t;
45 
46 typedef struct searchpath_s
47 {
48 	unsigned int	path_id;	/* identifier assigned to the game directory
49 					 *	Note that <install_dir>/game1 and
50 					 *	<userdir>/game1 have the same id. */
51 	char		filename[MAX_OSPATH];
52 	struct pack_s		*pack;	/* only one of filename / pack will be used */
53 	struct searchpath_s	*next;
54 } searchpath_t;
55 
56 static searchpath_t	*fs_searchpaths;
57 static searchpath_t	*fs_base_searchpaths;	/* without gamedirs */
58 
59 static const char	*fs_basedir;
60 static char	fs_gamedir[MAX_OSPATH];
61 static char	fs_userdir[MAX_OSPATH];
62 char	fs_gamedir_nopath[MAX_QPATH];
63 
64 unsigned int	gameflags;
65 
66 cvar_t	oem = {"oem", "0", CVAR_ROM};
67 cvar_t	registered = {"registered", "0", CVAR_ROM};
68 
69 typedef struct
70 {
71 	int	numfiles;
72 	unsigned int	crc;
73 	long	size;
74 	const char	*dirname;
75 } pakdata_t;
76 
77 #define	MAX_PAKDATA	5 /* pak0...4 */
78 static pakdata_t pakdata[MAX_PAKDATA] =
79 {
80 	{ 696,	34289,	22704056, "data1"	},	/* pak0.pak, registered, up-to-date, v1.11
81 							 *	MD5: c9675191e75dd25a3b9ed81ee7e05eff	*/
82 	{ 523,	2995 ,	75601170, "data1"	},	/* pak1.pak, registered, up-to-date, v1.11
83 							 *	MD5: c2ac5b0640773eed9ebe1cda2eca2ad0	*/
84 	{ 183,	4807 ,	17742721, "data1"	},	/* pak2.pak, oem (Matrox m3D bundle) v1.10
85 							 *	MD5: 99e0054861e94f66fc8e0e29416859c9	*/
86 	{ 245,	1478 ,	49089114, "portals"	},	/* pak3.pak, Portal of Praevus expansion pack
87 							 *	MD5: 77ae298dd0dcd16ab12f4a68067ff2c3	*/
88 	{ 102,	41062,	10780245, "hw"		}	/* pak4.pak, hexenworld, versions 0.14 - 0.15
89 							 *	MD5: 88109ee385d9723ac5f1015e034a44dd	*/
90 };
91 
92 static pakdata_t demo_pakdata[] =
93 {
94 	{ 797,	22780,	27750257, "data1"	}	/* pak0.pak, demo v1.11 from Nov. 1997
95 							 *	MD5: 8e598d82bf53436ed7a0e133aa4b9f09	*/
96 };
97 
98 static pakdata_t oem0_pakdata[] =	/* Continent of Blackmarsh */
99 {
100 	{ 697,	9787 ,	22720659, "data1"	}	/* pak0.pak, oem (Matrox m3D bundle) v1.10
101 							 *	MD5: 8c9c6118117baca7b9349d477403fcc0	*/
102 };
103 
104 static pakdata_t old_pakdata[] =
105 {
106 	{ 697,	53062,	21714275, "data1"	},	/* pak0.pak, original cdrom (1.03) version
107 							 *	MD5: b53c9391d16134cb3baddc1085f18683	*/
108 	{ 525,	47762,	76958474, "data1"	},	/* pak1.pak, original cdrom (1.03) version
109 							 *	MD5: 9a2010aafb9c0fe71c37d01292030270	*/
110 	{ 701,	20870,	23537707, "data1"	},	/* pak0.pak, original demo v0.42 from Aug. 1997
111 							 *	(h2.exe -> console -> version says 1.07!)
112 							 *	MD5: 208643a09193dafbca4b851762479438	*/
113 /* !!! FIXME:  I don't have the original v1.08 of Continent of Blackmarsh. I only know the file sizes.	*/
114 	{ -1,	0,	22719295, "data1"	},	/* pak0.pak, original oem (Matrox m3D) v1.08
115 							 *	MD5: ????????????????????????????????	*/
116 	{ -1,	0,	17739969, "data1"	},	/* pak2.pak, original oem (Matrox m3D) v1.08
117 							 *	MD5: ????????????????????????????????	*/
118 	{  98,	25864,	10678369, "hw"	},		/* pak4.pak, Hexen2World v0.11 (ugh..)
119 							 *	MD5: c311a30ac8ee1f112019723b4fe42268	*/
120 	{  40,	48258,	 3357888, "hw"	},		/* pak4.pak, Hexen2World v0.09 (ugh ugh ugh!)
121 							 *	MD5: 7708da4323f668cf9c71f99315704baa	*/
122 };
123 
124 /* this graphic needs to be in the pak file to use registered features */
125 static const unsigned short pop[] =
126 {
127 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
128 	0x0000, 0x0000, 0x6600, 0x0000, 0x0000, 0x0000, 0x6600, 0x0000,
129 	0x0000, 0x0066, 0x0000, 0x0000, 0x0000, 0x0000, 0x0067, 0x0000,
130 	0x0000, 0x6665, 0x0000, 0x0000, 0x0000, 0x0000, 0x0065, 0x6600,
131 	0x0063, 0x6561, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6563,
132 	0x0064, 0x6561, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6564,
133 	0x0064, 0x6564, 0x0000, 0x6469, 0x6969, 0x6400, 0x0064, 0x6564,
134 	0x0063, 0x6568, 0x6200, 0x0064, 0x6864, 0x0000, 0x6268, 0x6563,
135 	0x0000, 0x6567, 0x6963, 0x0064, 0x6764, 0x0063, 0x6967, 0x6500,
136 	0x0000, 0x6266, 0x6769, 0x6a68, 0x6768, 0x6a69, 0x6766, 0x6200,
137 	0x0000, 0x0062, 0x6566, 0x6666, 0x6666, 0x6666, 0x6562, 0x0000,
138 	0x0000, 0x0000, 0x0062, 0x6364, 0x6664, 0x6362, 0x0000, 0x0000,
139 	0x0000, 0x0000, 0x0000, 0x0062, 0x6662, 0x0000, 0x0000, 0x0000,
140 	0x0000, 0x0000, 0x0000, 0x0061, 0x6661, 0x0000, 0x0000, 0x0000,
141 	0x0000, 0x0000, 0x0000, 0x0000, 0x6500, 0x0000, 0x0000, 0x0000,
142 	0x0000, 0x0000, 0x0000, 0x0000, 0x6400, 0x0000, 0x0000, 0x0000
143 };
144 
145 static char *FSERR_MakePath_BUF (const char *caller, int linenum, int base,
146 				 char *buf, size_t siz, const char *path);
147 static char *FSERR_MakePath_VABUF (const char *caller, int linenum, int base,
148 				char *buf, size_t siz, const char *format, ...)
149 							FUNC_PRINTF(6,7);
150 static char *do_MakePath_VA (int base, int *error, char *buf, size_t siz,
151 					const char *format, va_list args)
152 							FUNC_PRINTF(5,0);
153 
154 /*
155 All of Quake's data access is through a hierchal file system, but the contents
156 of the file system can be transparently merged from several sources.
157 
158 The "base directory" is the path to the directory holding the exe and all game
159 directories.  The sys_* files pass this to host_init in quakeparms_t->basedir.
160 This can be overridden with the "-basedir" command line parm to allow code
161 debugging in a different directory.  The base directory is only used during
162 filesystem initialization.
163 
164 The "game directory" is the first tree on the search path and directory that
165 all generated files (savegames, screenshots, demos, config files) will be saved
166 to.  This can be overridden with the "-game" command line parameter.  The game
167 directory can never be changed while quake is executing.  This is a precacution
168 against having a malicious server instruct clients to write files over areas
169 they shouldn't.
170 */
171 
172 
check_known_paks(int paknum,int numfiles,unsigned short crc)173 static unsigned int check_known_paks (int paknum, int numfiles, unsigned short crc)
174 {
175 	if (paknum >= MAX_PAKDATA)
176 		return GAME_MODIFIED;
177 
178 	if (strcmp(fs_gamedir_nopath, pakdata[paknum].dirname) != 0)
179 		return GAME_MODIFIED;	/* Raven didn't ship like that */
180 
181 	if (numfiles != pakdata[paknum].numfiles)
182 	{
183 		switch (paknum)
184 		{
185 		case 0:	/* demo ?? */
186 			if (numfiles == demo_pakdata[0].numfiles &&
187 					crc == demo_pakdata[0].crc)
188 				return GAME_DEMO;
189 			/* oem ?? */
190 			if (numfiles == oem0_pakdata[0].numfiles &&
191 					crc == oem0_pakdata[0].crc)
192 				return GAME_OEM0;
193 			/* old version of demo ?? */
194 			if (numfiles == old_pakdata[2].numfiles &&
195 					crc == old_pakdata[2].crc)
196 				return GAME_OLD_DEMO;
197 			/* old cdrom version ?? */
198 			if (numfiles == old_pakdata[0].numfiles &&
199 					crc == old_pakdata[0].crc)
200 				return GAME_OLD_CDROM0;
201 			/* old oem version ?? */
202 			if (numfiles == old_pakdata[3].numfiles &&
203 					crc == old_pakdata[3].crc)
204 				return GAME_OLD_OEM0;
205 			/* not original: */
206 			return GAME_MODIFIED;
207 		case 1:	/* old cdrom version ?? */
208 			if (numfiles == old_pakdata[1].numfiles &&
209 					crc == old_pakdata[1].crc)
210 				return GAME_OLD_CDROM1;
211 			/* not original: */
212 			return GAME_MODIFIED;
213 		case 2:	/* old oem version ?? */
214 			if (numfiles == old_pakdata[4].numfiles &&
215 					crc == old_pakdata[4].crc)
216 				return GAME_OLD_OEM2;
217 			/* not original: */
218 			return GAME_MODIFIED;
219 		case 4:	/* old HW version ?? */
220 			if (numfiles == old_pakdata[5].numfiles &&
221 					crc == old_pakdata[5].crc)
222 				return GAME_HEXENWORLD;
223 			/* not original: */
224 			return GAME_MODIFIED;
225 		default:/* not original */
226 			return GAME_MODIFIED;
227 		}
228 	}
229 
230 	if (crc != pakdata[paknum].crc)
231 		return GAME_MODIFIED;	/* not original */
232 
233 	/* both crc and numfiles are good, we are still original */
234 	switch (paknum)
235 	{
236 	case 0:	/* pak0 of full version 1.11 */
237 		return GAME_REGISTERED0;
238 	case 1:	/* pak1 of full version 1.11 */
239 		return GAME_REGISTERED1;
240 	case 2:	/* bundle version */
241 		return GAME_OEM2;
242 	case 3:	/* mission pack */
243 		return GAME_PORTALS;
244 	case 4:	/* hexenworld */
245 		return GAME_HEXENWORLD;
246 	}
247 
248 	return GAME_MODIFIED;	/* we shouldn't reach here */
249 }
250 
251 /*
252 =================
253 FS_LoadPackFile
254 
255 Takes a path to a pak file.  Loads the header and directory.
256 =================
257 */
FS_LoadPackFile(const char * packfile,int paknum,qboolean base_fs)258 static pack_t *FS_LoadPackFile (const char *packfile, int paknum, qboolean base_fs)
259 {
260 	dpackheader_t	header;
261 	int	i, numpackfiles;
262 	pakfiles_t	*newfiles;
263 	pack_t		*pack;
264 	FILE		*packhandle;
265 	dpackfile_t	info[MAX_FILES_IN_PACK];
266 	unsigned short	crc;
267 
268 	packhandle = fopen (packfile, "rb");
269 	if (!packhandle)
270 		return NULL;
271 
272 	fread (&header, 1, sizeof(header), packhandle);
273 	if (header.id[0] != 'P' || header.id[1] != 'A' ||
274 	    header.id[2] != 'C' || header.id[3] != 'K')
275 	{
276 		Sys_Printf ("WARNING: %s is not a packfile, ignored\n", packfile);
277 		goto pak_error;
278 	}
279 
280 	header.dirofs = LittleLong (header.dirofs);
281 	header.dirlen = LittleLong (header.dirlen);
282 
283 	numpackfiles = header.dirlen / sizeof(dpackfile_t);
284 
285 	if (header.dirlen < 0 || header.dirofs < 0)
286 	{
287 		Sys_Error ("Invalid packfile %s (dirlen: %i, dirofs: %i)",
288 					packfile, header.dirlen, header.dirofs);
289 	}
290 	if (!numpackfiles)
291 	{
292 		Sys_Printf ("WARNING: %s has no files, ignored\n", packfile);
293 		goto pak_error;
294 	}
295 	if (numpackfiles > MAX_FILES_IN_PACK)
296 	{
297 		Sys_Printf ("WARNING: %s has %i files (max. allowed is %i), ignored\n",
298 					packfile, numpackfiles, MAX_FILES_IN_PACK);
299 		goto pak_error;
300 	}
301 
302 	newfiles = (pakfiles_t *) Z_Malloc (numpackfiles * sizeof(pakfiles_t), Z_MAINZONE);
303 
304 	fseek (packhandle, header.dirofs, SEEK_SET);
305 	fread (info,  1, header.dirlen, packhandle);
306 
307 	/* crc the directory */
308 	CRC_Init (&crc);
309 	for (i = 0; i < header.dirlen; i++)
310 		CRC_ProcessByte (&crc, ((byte *)info)[i]);
311 
312 	/* check for modifications */
313 	if (base_fs)
314 		gameflags |= check_known_paks (paknum, numpackfiles, crc);
315 	else	gameflags |= GAME_MODIFIED;
316 
317 	/* parse the directory */
318 	for (i = 0; i < numpackfiles; i++)
319 	{
320 		qerr_strlcpy(__thisfunc__, __LINE__, newfiles[i].name, info[i].name, MAX_QPATH);
321 		newfiles[i].filepos = LittleLong(info[i].filepos);
322 		newfiles[i].filelen = LittleLong(info[i].filelen);
323 	}
324 
325 	pack = (pack_t *) Z_Malloc (sizeof(pack_t), Z_MAINZONE);
326 	qerr_strlcpy(__thisfunc__, __LINE__, pack->filename, packfile, MAX_OSPATH);
327 	pack->handle = packhandle;
328 	pack->numfiles = numpackfiles;
329 	pack->files = newfiles;
330 
331 	Sys_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
332 	return pack;
333 pak_error:
334 	fclose (packhandle);
335 	return NULL;
336 }
337 
338 
339 /*
340 ================
341 FS_AddGameDirectory
342 
343 Sets fs_gamedir, fs_userdir and fs_gamedir_nopath.  adds the directory
344 to the head of the path, then loads and adds pak1.pak pak2.pak ...
345 ================
346 */
FS_AddGameDirectory(const char * dir,qboolean base_fs)347 static void FS_AddGameDirectory (const char *dir, qboolean base_fs)
348 {
349 	unsigned int	path_id;
350 	searchpath_t	*search;
351 	pack_t		*pak;
352 	char	pakfile[MAX_OSPATH];
353 	qboolean do_userdir = false;
354 	int	i;
355 
356 	qerr_strlcpy(__thisfunc__, __LINE__, fs_gamedir_nopath, dir,
357 						sizeof(fs_gamedir_nopath));
358 	FSERR_MakePath_BUF (__thisfunc__, __LINE__, FS_BASEDIR,
359 				fs_gamedir, sizeof(fs_gamedir), dir);
360 	FSERR_MakePath_BUF (__thisfunc__, __LINE__, FS_USERBASE,
361 				fs_userdir, sizeof(fs_userdir), dir);
362 
363 /* assign a path_id to this game directory */
364 	if (fs_searchpaths)
365 		path_id = fs_searchpaths->path_id << 1;
366 	else	path_id = 1U;
367 
368 #if DO_USERDIRS
369 add_pakfile:
370 #endif
371 /* add any pak files in the format pak0.pak pak1.pak, ...
372  * unlike Quake, Hexen II can't stop at first unavailable
373  * pak: the mission pack has only pak3, hw has only pak4.
374  */
375 	for (i = 0; i < 10; i++)
376 	{
377 		FSERR_MakePath_VABUF (__thisfunc__, __LINE__,
378 					(do_userdir) ? FS_USERDIR : FS_GAMEDIR,
379 					pakfile, sizeof(pakfile), "pak%i.pak", i);
380 		pak = FS_LoadPackFile (pakfile, i, base_fs);
381 		if (!pak) continue;
382 		search = (searchpath_t *) Z_Malloc (sizeof(searchpath_t), Z_MAINZONE);
383 		search->path_id = path_id;
384 		search->pack = pak;
385 		search->next = fs_searchpaths;
386 		fs_searchpaths = search;
387 	}
388 
389 /* add the directory itself to the search path.  unlike Quake,
390  * Hexen II does this ~after~ adding the pakfiles in this dir,
391  * so that the dir itself will be placed above the pakfiles in
392  * the search order which, in turn, will allow override files.
393  */
394 	search = (searchpath_t *) Z_Malloc (sizeof(searchpath_t), Z_MAINZONE);
395 	if (do_userdir)
396 		qerr_strlcpy(__thisfunc__, __LINE__, search->filename, fs_userdir, MAX_OSPATH);
397 	else	qerr_strlcpy(__thisfunc__, __LINE__, search->filename, fs_gamedir, MAX_OSPATH);
398 	search->path_id = path_id;
399 	search->next = fs_searchpaths;
400 	fs_searchpaths = search;
401 
402 	if (do_userdir)
403 		return;
404 	do_userdir = true;
405 
406 #if DO_USERDIRS
407 /* add user's directory to the search path and
408  * add any pak files in the user's directory.
409  */
410 	if (strcmp(fs_gamedir, fs_userdir))
411 	{
412 		Sys_mkdir (fs_userdir, true);
413 		goto add_pakfile;
414 	}
415 #endif
416 }
417 
418 /*
419 ================
420 FS_Gamedir
421 
422 Sets the gamedir and path to a different directory.
423 
424 Hexen II uses this for setting the game directory from a -game
425 command line argument.
426 
427 HexenWorld uses this to set the gamedir on both server and client
428 sides while the game is running: The client calls this upon every
429 map change from within CL_ParseServerData(), and the Server calls
430 this upon a gamedir command from within SV_Gamedir_f().
431 ================
432 */
433 #ifdef H2W
set_hw_dir(void)434 static inline void set_hw_dir (void) { /* helper for FS_Gamedir () */
435 	qerr_strlcpy(__thisfunc__, __LINE__, fs_gamedir_nopath, "hw",
436 					sizeof(fs_gamedir_nopath));
437 	FSERR_MakePath_BUF (__thisfunc__, __LINE__, FS_BASEDIR,
438 				fs_gamedir, sizeof(fs_gamedir), "hw");
439 	FSERR_MakePath_BUF (__thisfunc__, __LINE__, FS_USERBASE,
440 				fs_userdir, sizeof(fs_userdir), "hw");
441 	#ifdef SERVERONLY
442 	/* change the *gamedir serverinfo properly */
443 	Info_SetValueForStarKey (svs.info, "*gamedir", "hw", MAX_SERVERINFO_STRING);
444 	#endif
445 }
446 #endif
447 
FS_Gamedir(const char * dir)448 void FS_Gamedir (const char *dir)
449 {
450 	searchpath_t	*next;
451 
452 	if (!*dir || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\") || strstr(dir, ":"))
453 	{
454 		if (!host_initialized)
455 			Sys_Error ("gamedir should be a single directory name, not a path\n");
456 		else {
457 			Con_Printf("gamedir should be a single directory name, not a path\n");
458 			return;
459 		}
460 	}
461 
462 	if (!q_strcasecmp(fs_gamedir_nopath, dir))
463 		return;		/* still the same */
464 
465 /* free up any current game dir info: our top searchpath dir will be hw
466  * and any gamedirs set before by this very procedure will be removed.
467  * since hexen2 doesn't use this during game execution there will be no
468  * changes for it: it has portals or data1 at the top.
469  */
470 	while (fs_searchpaths != fs_base_searchpaths)
471 	{
472 		if (fs_searchpaths->pack)
473 		{
474 			fclose (fs_searchpaths->pack->handle);
475 			Z_Free (fs_searchpaths->pack->files);
476 			Z_Free (fs_searchpaths->pack);
477 		}
478 		next = fs_searchpaths->next;
479 		Z_Free (fs_searchpaths);
480 		fs_searchpaths = next;
481 	}
482 
483 /* flush all data, so it will be forced to reload */
484 #if !defined(SERVERONLY)
485 	Cache_Flush ();
486 #endif	/* SERVERONLY */
487 
488 /* check for reserved gamedirs */
489 	if (!q_strcasecmp(dir, "hw"))
490 	{
491 #if defined(H2W)
492 	/* that we reached here means the hw server decided to abandon
493 	 * whatever the previous mod it was running and went back to
494 	 * pure hw. weird.. do as he wishes anyway and adjust our variables. */
495 		set_hw_dir ();
496 #else	/* hexen2 case: */
497 	/* hw is reserved for hexenworld only. hexen2 shouldn't use it */
498 		Con_Printf ("WARNING: Gamedir not set to hw :\n"
499 			    "It is reserved for HexenWorld.\n");
500 #endif	/* H2W */
501 		return;
502 	}
503 
504 	if (!q_strcasecmp(dir, "portals"))
505 	{
506 	/* no hw server is supposed to set gamedir to portals
507 	 * and hw must be above portals in hierarchy. this is
508 	 * actually a hypothetical case.
509 	 * as for hexen2, it cannot reach here.  */
510 #ifdef H2W
511 		set_hw_dir ();
512 #endif
513 		return;
514 	}
515 
516 	if (!q_strcasecmp(dir, "data1"))
517 	{
518 	/* another hypothetical case: no hw mod is supposed to
519 	 * do this and hw must stay above data1 in hierarchy.
520 	 * as for hexen2, it can only reach here by a silly
521 	 * command line argument like -game data1, ignore it. */
522 #ifdef H2W
523 		set_hw_dir ();
524 #endif
525 		return;
526 	}
527 
528 /* a new gamedir: let's set it here. */
529 	FS_AddGameDirectory(dir, false);
530 #if defined(H2W) && defined(SERVERONLY)
531 /* change the *gamedir serverinfo properly */
532 	Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING);
533 #endif
534 }
535 
536 
537 /*
538 ==============================================================================
539 
540 FILE I/O within QFS
541 
542 ==============================================================================
543 */
544 
545 size_t		fs_filesize;	/* size of the last file opened through QFS */
546 int		file_from_pak;	/* ZOID: global indicating that file came from a pak */
547 
548 
549 /*
550 ===========
551 FS_CopyFile
552 
553 Copies the FROMPATH file as TOPATH file, creating any dirs needed.
554 Used for saving the game. Returns 0 on success, non-zero on error.
555 ===========
556 */
FS_CopyFile(const char * frompath,const char * topath)557 int FS_CopyFile (const char *frompath, const char *topath)
558 {
559 	char	*tmp;
560 	int	err;
561 
562 	if (!frompath || !topath)
563 	{
564 		Con_Printf ("%s: null input\n", __thisfunc__);
565 		return 1;
566 	}
567 	/* create directories up to the dest file */
568 	tmp = Z_Strdup (topath);
569 	err = FS_CreatePath(tmp);
570 	Z_Free (tmp);
571 	if (err != 0)
572 	{
573 		Con_Printf ("%s: unable to create directory\n", __thisfunc__);
574 		return err;
575 	}
576 
577 	err = Sys_CopyFile (frompath, topath);
578 	return err;
579 }
580 
581 #define	COPY_READ_BUFSIZE		8192	/* BUFSIZ */
FS_WriteFileFromHandle(FILE * fromfile,const char * topath,size_t size)582 int FS_WriteFileFromHandle (FILE *fromfile, const char *topath, size_t size)
583 {
584 	char	buf[COPY_READ_BUFSIZE];
585 	FILE	*out;
586 /*	off_t	remaining, count;*/
587 	size_t	remaining, count;
588 	char	*tmp;
589 	int	err;
590 
591 	if (!fromfile || !topath)
592 	{
593 		Con_Printf ("%s: null input\n", __thisfunc__);
594 		return 1;
595 	}
596 
597 	/* create directories up to the dest file */
598 	tmp = Z_Strdup (topath);
599 	err = FS_CreatePath(tmp);
600 	Z_Free (tmp);
601 	if (err != 0)
602 	{
603 		Con_Printf ("%s: unable to create directory\n", __thisfunc__);
604 		return err;
605 	}
606 
607 	out = fopen (topath, "wb");
608 	if (!out)
609 	{
610 		Con_Printf ("%s: unable to create %s\n", topath, __thisfunc__);
611 		return 1;
612 	}
613 
614 	remaining = size;
615 	while (remaining)
616 	{
617 		if (remaining < sizeof(buf))
618 			count = remaining;
619 		else	count = sizeof(buf);
620 
621 		if (fread(buf, 1, count, fromfile) != count)
622 			break;
623 		if (fwrite(buf, 1, count, out) != count)
624 			break;
625 
626 		remaining -= count;
627 	}
628 
629 	fclose (out);
630 
631 	return (remaining == 0)? 0 : 1;
632 }
633 
634 /*
635 ============
636 FS_WriteFile
637 
638 The filename will be prefixed by the current game directory
639 Returns 0 on success, 1 on error.
640 ============
641 */
FS_WriteFile(const char * filename,const void * data,size_t len)642 int FS_WriteFile (const char *filename, const void *data, size_t len)
643 {
644 	FILE	*f;
645 	char	name[MAX_OSPATH];
646 	size_t	size;
647 	int	err;
648 
649 	FS_MakePath_BUF (FS_USERDIR, &err, name, sizeof(name), filename);
650 	if (err)
651 	{
652 		Host_Error("%s: %d: string buffer overflow!", __thisfunc__, __LINE__);
653 		return 1;
654 	}
655 
656 	f = fopen (name, "wb");
657 	if (!f)
658 	{
659 		Con_Printf ("Error opening %s\n", filename);
660 		return 1;
661 	}
662 
663 	Sys_Printf ("%s: %s\n", __thisfunc__, name);
664 	size = fwrite (data, 1, len, f);
665 	fclose (f);
666 	if (size != len)
667 	{
668 		Con_Printf ("Error in writing %s\n", filename);
669 		return 1;
670 	}
671 	return 0;
672 }
673 
674 
675 /*
676 ============
677 FS_CreatePath
678 
679 Creates directory under user's path, making parent directories
680 as needed. The path must either be a path to a file, or, if the
681 full path is meant to be created, it must have the trailing path
682 seperator. Returns 0 on success, non-zero on error.
683 ============
684 */
FS_CreatePath(char * path)685 int FS_CreatePath (char *path)
686 {
687 	char	*ofs, c;
688 	int	err = 0;
689 	size_t	offset;
690 
691 	if (!path || !*path)
692 	{
693 		Con_Printf ("%s: no path!\n", __thisfunc__);
694 		return 1;
695 	}
696 
697 	if (strstr(path, ".."))
698 	{
699 		Con_Printf ("Relative pathnames are not allowed.\n");
700 		return 1;
701 	}
702 
703 	offset = strlen(host_parms->userdir);
704 	if (offset && strstr(path, host_parms->userdir) != path)
705 	{
706 		Sys_Error ("Attempted to create a directory out of user's path");
707 		return 1;
708 	}
709 
710 	ofs = path + offset;
711 	if (*ofs == '\0')	/* not necessarily an error. */
712 		return 0;
713 	/* check for the path separator after the userdir. */
714 	if (IS_DIR_SEPARATOR(*ofs))
715 		ofs++;
716 	else if (offset)
717 	{
718 		/* if the userdir itself has no trailing DIRSEP
719 		 * either, then it is a bad path: */
720 		if (!IS_DIR_SEPARATOR(host_parms->userdir[offset - 1]))
721 		{
722 			Con_Printf ("%s: bad path\n", __thisfunc__);
723 			return 1;
724 		}
725 	}
726 
727 	for ( ; *ofs; ofs++)
728 	{
729 		c = *ofs;
730 		if (IS_DIR_SEPARATOR(c))
731 		{	/* create the directory */
732 			*ofs = 0;
733 			err = Sys_mkdir (path, false);
734 			*ofs = c;
735 			if (err)
736 				break;
737 		}
738 	}
739 
740 	return err;
741 }
742 
743 
744 /*
745 ===========
746 FS_OpenFile
747 
748 Finds the file in the search path, returns fs_filesize.
749 ===========
750 */
FS_OpenFile(const char * filename,FILE ** file,unsigned int * path_id)751 size_t FS_OpenFile (const char *filename, FILE **file, unsigned int *path_id)
752 {
753 	searchpath_t	*search;
754 	pack_t		*pak;
755 	char	ospath[MAX_OSPATH];
756 	int	i;
757 
758 	file_from_pak = 0;
759 
760 	/* search through the path, one element at a time */
761 	for (search = fs_searchpaths ; search ; search = search->next)
762 	{
763 		if (search->pack)	/* look through all the pak file elements */
764 		{
765 			pak = search->pack;
766 			for (i = 0; i < pak->numfiles; i++)
767 			{
768 				if (strcmp(pak->files[i].name, filename) != 0)
769 					continue;
770 				/* found it! */
771 				fs_filesize = (size_t) pak->files[i].filelen;
772 				file_from_pak = 1;
773 				if (path_id)
774 					*path_id = search->path_id;
775 				if (!file) /* for FS_FileExists() */
776 					return fs_filesize;
777 				/* open a new file on the pakfile */
778 				*file = fopen (pak->filename, "rb");
779 				if (!*file)
780 					Sys_Error ("Couldn't reopen %s", pak->filename);
781 				fseek (*file, pak->files[i].filepos, SEEK_SET);
782 				return fs_filesize;
783 			}
784 		}
785 		else	/* check a file in the directory tree */
786 		{
787 			q_snprintf (ospath, sizeof(ospath), "%s/%s",search->filename, filename);
788 			fs_filesize = (size_t) Sys_filesize (ospath);
789 			if (fs_filesize == (size_t)-1)
790 				continue;
791 			if (path_id)
792 				*path_id = search->path_id;
793 			if (!file) /* for FS_FileExists() */
794 				return fs_filesize;
795 			*file = fopen (ospath, "rb");
796 			if (!*file)
797 				Sys_Error ("Couldn't reopen %s", ospath);
798 			return fs_filesize;
799 		}
800 	}
801 
802 	Sys_DPrintf ("%s: can't find %s\n", __thisfunc__, filename);
803 
804 	if (file) *file = NULL;
805 	fs_filesize = (size_t)-1;
806 	return fs_filesize;
807 }
808 
809 /*
810 ===========
811 FS_FileExists
812 
813 Returns whether the file is found in the hexen2 filesystem.
814 ===========
815 */
FS_FileExists(const char * filename,unsigned int * path_id)816 qboolean FS_FileExists (const char *filename, unsigned int *path_id)
817 {
818 	size_t ret = FS_OpenFile (filename, NULL, path_id);
819 	return (ret == (size_t)-1) ? false : true;
820 }
821 
822 /*
823 ============
824 FS_FileInGamedir
825 
826 Reports the existance of a file with read permissions in
827 fs_gamedir or fs_userdir. *NOT* for files in pakfiles!
828 ============
829 */
FS_FileInGamedir(const char * filename)830 qboolean FS_FileInGamedir (const char *filename)
831 {
832 	int	ret;
833 
834 	ret = Sys_FileType(FS_MakePath(FS_USERDIR, NULL, filename));
835 	if (ret & FS_ENT_FILE)
836 		return true;
837 	ret = Sys_FileType(FS_MakePath(FS_GAMEDIR, NULL, filename));
838 	if (ret & FS_ENT_FILE)
839 		return true;
840 
841 	return false;
842 }
843 
844 /*
845 ============
846 FS_LoadFile
847 
848 Filename are reletive to the quake directory.
849 Allways appends a 0 byte to the loaded data.
850 ============
851 */
852 #define	LOADFILE_ZONE		0
853 #define	LOADFILE_HUNK		1
854 #define	LOADFILE_TEMPHUNK	2
855 #define	LOADFILE_CACHE		3
856 #define	LOADFILE_STACK		4
857 #define	LOADFILE_MALLOC		5
858 
859 static byte	*loadbuf;
860 #if !defined(SERVERONLY)
861 static cache_user_t *loadcache;
862 #endif	/* SERVERONLY */
863 static size_t		loadsize;
864 static int		zone_num;
865 
866 #if defined (SERVERONLY)
867 #define Draw_BeginDisc()
868 #define Draw_EndDisc()
869 #endif	/* SERVERONLY */
870 
FS_LoadFile(const char * path,int usehunk,unsigned int * path_id)871 static byte *FS_LoadFile (const char *path, int usehunk, unsigned int *path_id)
872 {
873 	FILE	*h;
874 	byte	*buf;
875 	char	base[32];
876 	size_t	len;
877 
878 /* look for it in the filesystem or pack files */
879 	len = FS_OpenFile (path, &h, path_id);
880 	if (!h)
881 		return NULL;
882 
883 /* extract the file's base name for hunk tag */
884 	COM_FileBase (path, base, sizeof(base));
885 	buf = NULL;	/* quiet compiler warning */
886 
887 	switch (usehunk)
888 	{
889 	case LOADFILE_HUNK:
890 		buf = (byte *) Hunk_AllocName (len+1, base);
891 		break;
892 	case LOADFILE_TEMPHUNK:
893 		buf = (byte *) Hunk_TempAlloc (len+1);
894 		break;
895 	case LOADFILE_ZONE:
896 		buf = (byte *) Z_Malloc (len+1, zone_num);
897 		break;
898 #if !defined(SERVERONLY)
899 	case LOADFILE_CACHE:
900 		buf = (byte *) Cache_Alloc (loadcache, len+1, base);
901 		break;
902 #endif	/* SERVERONLY */
903 	case LOADFILE_STACK:
904 		if (len < loadsize)
905 			buf = loadbuf;
906 		else
907 			buf = (byte *) Hunk_TempAlloc (len+1);
908 		break;
909 	case LOADFILE_MALLOC:
910 		buf = (byte *) malloc (len+1);
911 		break;
912 	default:
913 		Sys_Error ("%s: bad usehunk", __thisfunc__);
914 	}
915 
916 	if (!buf)
917 		Sys_Error ("%s: not enough space for %s", __thisfunc__, path);
918 
919 	((byte *)buf)[len] = 0;
920 
921 	Draw_BeginDisc ();
922 	fread (buf, 1, len, h);
923 	fclose (h);
924 	Draw_EndDisc ();
925 
926 	return buf;
927 }
928 
FS_LoadHunkFile(const char * path,unsigned int * path_id)929 byte *FS_LoadHunkFile (const char *path, unsigned int *path_id)
930 {
931 	return FS_LoadFile (path, LOADFILE_HUNK, path_id);
932 }
933 
FS_LoadZoneFile(const char * path,int zone_id,unsigned int * path_id)934 byte *FS_LoadZoneFile (const char *path, int zone_id, unsigned int *path_id)
935 {
936 	zone_num = zone_id;
937 	return FS_LoadFile (path, LOADFILE_ZONE, path_id);
938 }
939 
FS_LoadTempFile(const char * path,unsigned int * path_id)940 byte *FS_LoadTempFile (const char *path, unsigned int *path_id)
941 {
942 	return FS_LoadFile (path, LOADFILE_TEMPHUNK, path_id);
943 }
944 
945 #if !defined(SERVERONLY)
FS_LoadCacheFile(const char * path,struct cache_user_s * cu,unsigned int * path_id)946 void FS_LoadCacheFile (const char *path, struct cache_user_s *cu, unsigned int *path_id)
947 {
948 	loadcache = cu;
949 	FS_LoadFile (path, LOADFILE_CACHE, path_id);
950 }
951 #endif	/* SERVERONLY */
952 
953 /* uses temp hunk if larger than bufsize */
FS_LoadStackFile(const char * path,void * buffer,size_t bufsize,unsigned int * path_id)954 byte *FS_LoadStackFile (const char *path, void *buffer, size_t bufsize, unsigned int *path_id)
955 {
956 	byte	*buf;
957 
958 	loadbuf = (byte *)buffer;
959 	loadsize = bufsize;
960 	buf = FS_LoadFile (path, LOADFILE_STACK, path_id);
961 
962 	return buf;
963 }
964 
965 /* returns malloc'd memory */
FS_LoadMallocFile(const char * path,unsigned int * path_id)966 byte *FS_LoadMallocFile (const char *path, unsigned int *path_id)
967 {
968 	return FS_LoadFile (path, LOADFILE_MALLOC, path_id);
969 }
970 
971 
972 /*
973 ==============================================================================
974 
975 MISC CONSOLE COMMANDS
976 
977 ==============================================================================
978 */
979 
980 /*
981 ============
982 FS_Path_f
983 Prints the search path to the console
984 ============
985 */
FS_Path_f(void)986 static void FS_Path_f (void)
987 {
988 	searchpath_t	*s;
989 
990 	Con_Printf ("Current search path:\n");
991 	for (s = fs_searchpaths ; s ; s = s->next)
992 	{
993 		if (s == fs_base_searchpaths)
994 			Con_Printf ("----------\n");
995 		if (s->pack)
996 			Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
997 		else
998 			Con_Printf ("%s\n", s->filename);
999 	}
1000 }
1001 
1002 /*
1003 ===========
1004 processMapname: Callback for FS_Maplist_f.
1005 Returns 0 if a name is skipped, the current
1006 number of names added to the list if the name
1007 is added, or -1 upon failures.
1008 ===========
1009 */
1010 #if !defined(SERVERONLY)	/* dedicated servers dont need this command */
1011 
1012 #define MAX_NUMMAPS	256	/* max number of maps to list */
1013 static int	map_count = 0;
1014 static char	*maplist[MAX_NUMMAPS];
1015 
processMapname(const char * mapname,const char * partial,size_t len_partial)1016 static int processMapname (const char *mapname, const char *partial, size_t len_partial)
1017 {
1018 	char	cur_name[MAX_QPATH];
1019 	int	j, len;
1020 
1021 	if (map_count >= MAX_NUMMAPS)
1022 	{
1023 		Con_Printf ("WARNING: reached maximum number of maps to list\n");
1024 		return -1;
1025 	}
1026 
1027 	if (len_partial)
1028 	{
1029 		if (q_strncasecmp(partial, mapname, len_partial) != 0)
1030 			return 0;	/* doesn't match the prefix. skip. */
1031 	}
1032 
1033 	len = q_strlcpy (cur_name, mapname, sizeof(cur_name)) - 4; /* -4 to kill ".bsp" */
1034 	if (len <= 0)
1035 		return 0;
1036 	if (q_strcasecmp(&cur_name[len], ".bsp") != 0)
1037 		return 0;
1038 
1039 	cur_name[len] = 0;
1040 
1041 	for (j = 0; j < map_count; j++)
1042 	{
1043 		if (! q_strcasecmp(maplist[j], cur_name))
1044 			return 0;	/* duplicated name. skip. */
1045 	}
1046 
1047 	/* add to the maplist */
1048 	maplist[map_count] = Z_Strdup (cur_name);
1049 	return (++map_count);
1050 }
1051 
1052 /*
1053 ===========
1054 FS_Maplist_f
1055 Prints map filenames to the console
1056 ===========
1057 */
FS_Maplist_f(void)1058 static void FS_Maplist_f (void)
1059 {
1060 	searchpath_t	*search;
1061 	const char	*prefix;
1062 	size_t		preLen;
1063 
1064 	if (Cmd_Argc() > 1)
1065 	{
1066 		prefix = Cmd_Argv(1);
1067 		preLen = strlen(prefix);
1068 	}
1069 	else
1070 	{
1071 		preLen = 0;
1072 		prefix = NULL;
1073 	}
1074 
1075 	for (search = fs_searchpaths; search; search = search->next)
1076 	{
1077 		if (search->pack)
1078 		{
1079 			int i = 0;
1080 			for (; i < search->pack->numfiles; ++i)
1081 			{
1082 				if (strncmp("maps/", search->pack->files[i].name, 5) != 0)
1083 					continue;
1084 				if (processMapname(search->pack->files[i].name + 5, prefix, preLen) < 0)
1085 					goto done;
1086 			}
1087 		}
1088 		else
1089 		{
1090 			const char *findname = Sys_FindFirstFile(va("%s/maps",search->filename), "*.bsp");
1091 			while (findname)
1092 			{
1093 				if (processMapname(findname, prefix, preLen) < 0)
1094 				{
1095 					Sys_FindClose ();
1096 					goto done;
1097 				}
1098 				findname = Sys_FindNextFile ();
1099 			}
1100 			Sys_FindClose ();
1101 		}
1102 	}
1103 
1104 done:
1105 	if (!map_count)
1106 	{
1107 		Con_Printf ("No maps found.\n\n");
1108 		return;
1109 	}
1110 
1111 	Con_Printf ("Found %d maps:\n\n", map_count);
1112 	/* sort the list */
1113 	if (map_count > 1)
1114 		qsort (maplist, map_count, sizeof(char *), COM_StrCompare);
1115 	Con_ShowList (map_count, (const char**)maplist);
1116 	Con_Printf ("\n");
1117 
1118 	/* free the memory and zero map_count */
1119 	while (map_count)
1120 		Z_Free (maplist[--map_count]);
1121 }
1122 #endif	/* SERVERONLY */
1123 
1124 
1125 /*
1126 ==============================================================================
1127 
1128 INIT
1129 
1130 ==============================================================================
1131 */
1132 
1133 /*
1134 ================
1135 CheckRegistered
1136 
1137 Looks for the pop.txt file and verifies it.
1138 Sets the registered flag.
1139 ================
1140 */
CheckRegistered(void)1141 static int CheckRegistered (void)
1142 {
1143 	FILE	*h;
1144 	unsigned short	check[128];
1145 	int	i;
1146 
1147 	FS_OpenFile("gfx/pop.lmp", &h, NULL);
1148 	if (!h)
1149 		return -1;
1150 
1151 	fread (check, 1, sizeof(check), h);
1152 	fclose (h);
1153 
1154 	for (i = 0; i < 128; i++)
1155 	{
1156 		if (pop[i] != (unsigned short)BigShort(check[i]))
1157 		{
1158 			Sys_Printf ("Corrupted data file\n");
1159 			return -1;
1160 		}
1161 	}
1162 
1163 	return 0;
1164 }
1165 
1166 /*
1167 ================
1168 FS_Init
1169 ================
1170 */
FS_Init(void)1171 void FS_Init (void)
1172 {
1173 	qboolean check_portals = false;
1174 	int	i;
1175 
1176 	Cvar_RegisterVariable (&oem);
1177 	Cvar_RegisterVariable (&registered);
1178 
1179 	Cmd_AddCommand ("path", FS_Path_f);
1180 #if !defined(SERVERONLY)
1181 	Cmd_AddCommand ("maplist", FS_Maplist_f);
1182 #endif	/* SERVERONLY */
1183 
1184 /* -basedir <path> overrides the system supplied base directory */
1185 	i = COM_CheckParm ("-basedir");
1186 	if (i && i < com_argc-1)
1187 	{
1188 		fs_basedir = com_argv[i+1];
1189 		if (!*fs_basedir) Sys_Error("Bad argument to -basedir");
1190 #if !DO_USERDIRS
1191 		host_parms->userdir = com_argv[i+1];
1192 #endif
1193 		Sys_Printf ("%s: basedir changed to: %s\n", __thisfunc__, fs_basedir);
1194 	}
1195 	else
1196 	{
1197 		fs_basedir = host_parms->basedir;
1198 	}
1199 
1200 /* step 1: start up with data1 by default */
1201 	FS_AddGameDirectory ("data1", true);
1202 
1203 	if (gameflags & GAME_REGISTERED0 && gameflags & GAME_REGISTERED1)
1204 		gameflags |= GAME_REGISTERED;
1205 	if (gameflags & GAME_OEM0 && gameflags & GAME_OEM2)
1206 		gameflags |= GAME_OEM;
1207 	if (gameflags & GAME_OLD_CDROM0 && gameflags & GAME_OLD_CDROM1)
1208 		gameflags |= GAME_REGISTERED_OLD;
1209 	if (gameflags & GAME_OLD_OEM0 && gameflags & GAME_OLD_OEM2)
1210 		gameflags |= GAME_OLD_OEM;
1211 	/* check for bad installations (mix'n'match data): */
1212 	if ((gameflags & GAME_REGISTERED0 && gameflags & GAME_OLD_CDROM1) ||
1213 	    (gameflags & GAME_REGISTERED1 && gameflags & GAME_OLD_CDROM0) ||
1214 	    (gameflags & (GAME_OEM2|GAME_OLD_OEM2) && gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD|GAME_DEMO|GAME_OLD_DEMO)) ||
1215 	    (gameflags & (GAME_REGISTERED1|GAME_OLD_CDROM1) &&
1216 					 gameflags & (GAME_DEMO|GAME_OLD_DEMO|GAME_OEM0|GAME_OLD_OEM0|GAME_OEM2|GAME_OLD_OEM2)))
1217 	{
1218 		Sys_Error ("Bad Hexen II installation: mixed data from incompatible versions");
1219 	}
1220 #if !ENABLE_OLD_DEMO
1221 	if (gameflags & GAME_OLD_DEMO)
1222 		Sys_Error ("Old version of Hexen II demo isn't supported");
1223 #endif	/* OLD_DEMO */
1224 #if !ENABLE_OLD_RETAIL
1225 	/* check if we have 1.11 versions of pak0.pak and pak1.pak */
1226 	if (gameflags & (GAME_OLD_CDROM0|GAME_OLD_CDROM1|GAME_OLD_OEM0|GAME_OLD_OEM2))
1227 		Sys_Error ("You must patch your installation with Raven's 1.11 update");
1228 #endif	/* OLD_RETAIL */
1229 
1230 	/* finish the base filesystem setup */
1231 	if (gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD))
1232 	{
1233 		Cvar_SetROM ("registered", "1");
1234 		Sys_Printf ("Playing the registered version.\n");
1235 	}
1236 	else if (gameflags & GAME_OEM)
1237 	{
1238 		Cvar_SetROM ("oem", "1");
1239 		Sys_Printf ("Playing the oem (Matrox m3D bundle) version \"Continent of Blackmarsh\"\n");
1240 	}
1241 	else if (gameflags & (GAME_DEMO|GAME_OLD_DEMO))
1242 	{
1243 		Sys_Printf ("Playing the demo version.\n");
1244 	}
1245 	else
1246 	{
1247 	/* no proper Raven data: it's best to error out here */
1248 		Sys_Error ("Unable to find a proper Hexen II installation");
1249 	}
1250 	if (gameflags & (GAME_OLD_DEMO|GAME_REGISTERED_OLD|GAME_OLD_OEM))
1251 		Sys_Printf ("Using old/unsupported, pre-1.11 version pak files.\n");
1252 	if (gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD))
1253 	{
1254 		if (CheckRegistered() != 0)
1255 			Sys_Error ("Unable to verify retail version data.");
1256 	}
1257 #if !(defined(H2W) && defined(SERVERONLY))
1258 	if (gameflags & GAME_MODIFIED && !(gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD)))
1259 		Sys_Error ("You must have the full version of Hexen II to play modified games");
1260 #endif
1261 
1262 /* step 2: portals directory (mission pack) */
1263 #if defined(H2MP)
1264 	if (! COM_CheckParm ("-noportals"))
1265 		check_portals = true;
1266 	if (check_portals && !(gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD)))
1267 		Sys_Error ("Portal of Praevus requires registered version of Hexen II");
1268 #elif defined(H2W)
1269 	if (! COM_CheckParm ("-noportals") && gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD))
1270 		check_portals = true;
1271 #else
1272 	/* see if the user wants mission pack support */
1273 	check_portals = (COM_CheckParm ("-portals")) ||
1274 			(COM_CheckParm ("-missionpack")) ||
1275 			(COM_CheckParm ("-h2mp"));
1276 	i = COM_CheckParm ("-game");
1277 	if (i && i < com_argc-1)
1278 	{
1279 		if (!q_strcasecmp(com_argv[i+1], "portals"))
1280 			check_portals = true;
1281 	}
1282 	if (check_portals && !(gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD)))
1283 		Sys_Error ("Portal of Praevus requires registered version of Hexen II");
1284 #endif
1285 #if !defined(H2W)
1286 	if (sv_protocol == PROTOCOL_RAVEN_111 && check_portals)
1287 		Sys_Error ("Old protocol request not compatible with the Mission Pack");
1288 #endif
1289 
1290 	if (check_portals)
1291 	{
1292 #if defined(H2W)
1293 		searchpath_t	*mark = fs_searchpaths;
1294 #endif
1295 		FS_AddGameDirectory ("portals", true);
1296 		if (! (gameflags & GAME_PORTALS))
1297 		{
1298 #if !defined(H2W)
1299 			Sys_Error ("Missing or invalid mission pack installation\n");
1300 #else
1301 			/* back out searchpaths from invalid mission pack
1302 			 * installations because the portals directory is
1303 			 * reserved for the mission pack */
1304 			searchpath_t	*next;
1305 			Sys_Printf ("Missing or invalid mission pack installation\n");
1306 			while (fs_searchpaths != mark)
1307 			{
1308 				if (fs_searchpaths->pack)
1309 				{
1310 					fclose (fs_searchpaths->pack->handle);
1311 					Z_Free (fs_searchpaths->pack->files);
1312 					Z_Free (fs_searchpaths->pack);
1313 					Sys_Printf ("Removed packfile %s\n", fs_searchpaths->pack->filename);
1314 				}
1315 				else
1316 				{
1317 					Sys_Printf ("Removed path %s\n", fs_searchpaths->filename);
1318 				}
1319 				next = fs_searchpaths->next;
1320 				Z_Free (fs_searchpaths);
1321 				fs_searchpaths = next;
1322 			}
1323 			fs_searchpaths = mark;
1324 			/* back to data1 */
1325 			FS_MakePath_BUF (FS_BASEDIR, NULL, fs_gamedir, sizeof(fs_gamedir), "data1");
1326 			FS_MakePath_BUF (FS_USERBASE,NULL, fs_userdir, sizeof(fs_userdir), "data1");
1327 #endif	/* H2W */
1328 		}
1329 	}
1330 
1331 /* step 3: hw directory (hexenworld) */
1332 #if defined(H2W)
1333 	FS_AddGameDirectory ("hw", true);
1334 	/* error out if GAME_HEXENWORLD isn't set */
1335 	if (!(gameflags & GAME_HEXENWORLD))
1336 		Sys_Error ("You must have the HexenWorld data installed");
1337 #endif	/* H2W */
1338 
1339 /* this is the end of our base searchpath:
1340  * any set gamedirs, such as those from -game commandline
1341  * arguments, from exec'ed configs or the ones dictated by
1342  * the server, will be freed up to here upon a new gamedir
1343  * command */
1344 	fs_base_searchpaths = fs_searchpaths;
1345 
1346 	i = COM_CheckParm ("-game");
1347 	if (i != 0)
1348 	{
1349 		/* only registered versions can do -game */
1350 		if (! (gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD)))
1351 			Sys_Error ("You must have the full version of Hexen II to play modified games");
1352 		/* add basedir/gamedir as an override game */
1353 		if (i < com_argc - 1)
1354 			FS_Gamedir (com_argv[i+1]);
1355 	}
1356 }
1357 
1358 #define	FS_NUM_BUFFS	4
1359 #define	FS_BUFFERLEN	1024
1360 
get_fs_buffer(void)1361 static char *get_fs_buffer(void)
1362 {
1363 	static char fs_buffers[FS_NUM_BUFFS][FS_BUFFERLEN];
1364 	static int buffer_idx = 0;
1365 	buffer_idx = (buffer_idx + 1) & (FS_NUM_BUFFS - 1);
1366 	return fs_buffers[buffer_idx];
1367 }
1368 
init_MakePath(int base,char * buf,size_t siz)1369 static int init_MakePath (int base, char *buf, size_t siz)
1370 {
1371 	int	len;
1372 
1373 	switch (base)
1374 	{
1375 	case FS_USERDIR:
1376 		len = q_strlcpy(buf, fs_userdir, siz);
1377 		break;
1378 	case FS_GAMEDIR:
1379 		len = q_strlcpy(buf, fs_gamedir, siz);
1380 		break;
1381 	case FS_USERBASE:
1382 		len = q_strlcpy(buf, host_parms->userdir, siz);
1383 		break;
1384 	case FS_BASEDIR:
1385 		len = q_strlcpy(buf, fs_basedir, siz);
1386 		break;
1387 	default:
1388 		Sys_Error ("%s: Bad FS_BASE", __thisfunc__);
1389 		return -1;
1390 	}
1391 
1392 	if (len >= (int)siz - 1)
1393 		return -1;
1394 	if (len && !IS_DIR_SEPARATOR(buf[len - 1]))
1395 		buf[len++] = DIR_SEPARATOR_CHAR;
1396 	buf[len] = '\0';
1397 	return len;
1398 }
1399 
do_MakePath(int base,int * error,char * buf,size_t siz,const char * path)1400 static char *do_MakePath (int base, int *error, char *buf, size_t siz, const char *path)
1401 {
1402 	int	len;
1403 
1404 	len = init_MakePath(base, buf, siz);
1405 	if (len < 0) goto _bad;
1406 
1407 	len = q_strlcat(buf, path, siz);
1408 	if (len < (int)siz) {
1409 		if (error) *error = 0;
1410 	} else {
1411 	_bad:
1412 		if (error) *error = 1;
1413 		Con_DPrintf("%s: overflow (string truncated)\n", __thisfunc__);
1414 	}
1415 
1416 	return buf;
1417 }
1418 
do_MakePath_VA(int base,int * error,char * buf,size_t siz,const char * format,va_list args)1419 static char *do_MakePath_VA (int base, int *error, char *buf, size_t siz,
1420 					const char *format, va_list args)
1421 {
1422 	int	len, ret;
1423 
1424 	len = init_MakePath(base, buf, siz);
1425 	if (len < 0) goto _bad;
1426 
1427 	ret = q_vsnprintf(&buf[len], siz - len, format, args);
1428 	if (ret < (int)siz - len) {
1429 		if (error) *error = 0;
1430 	} else {
1431 	_bad:
1432 		if (error) *error = 1;
1433 		Con_DPrintf("%s: overflow (string truncated)\n", __thisfunc__);
1434 	}
1435 
1436 	return buf;
1437 }
1438 
FSERR_MakePath_BUF(const char * caller,int linenum,int base,char * buf,size_t siz,const char * path)1439 static char *FSERR_MakePath_BUF (const char *caller, int linenum, int base,
1440 				 char *buf, size_t siz, const char *path)
1441 {
1442 	int	err;
1443 	char	*p;
1444 
1445 	p = do_MakePath(base, &err, buf, siz, path);
1446 
1447 	if (err) Sys_Error("%s: %d: string buffer overflow!", caller, linenum);
1448 	return p;
1449 }
1450 
FSERR_MakePath_VABUF(const char * caller,int linenum,int base,char * buf,size_t siz,const char * format,...)1451 static char *FSERR_MakePath_VABUF (const char *caller, int linenum, int base,
1452 				char *buf, size_t siz, const char *format, ...)
1453 {
1454 	va_list	argptr;
1455 	int	err;
1456 	char	*p;
1457 
1458 	va_start (argptr, format);
1459 	p = do_MakePath_VA(base, &err, buf, siz, format, argptr);
1460 	va_end (argptr);
1461 
1462 	if (err) Sys_Error("%s: %d: string buffer overflow!", caller, linenum);
1463 	return p;
1464 }
1465 
FS_MakePath(int base,int * error,const char * path)1466 char *FS_MakePath (int base, int *error, const char *path)
1467 {
1468 	return do_MakePath(base, error, get_fs_buffer(), FS_BUFFERLEN, path);
1469 }
1470 
FS_MakePath_BUF(int base,int * error,char * buf,size_t siz,const char * path)1471 char *FS_MakePath_BUF (int base, int *error, char *buf, size_t siz, const char *path)
1472 {
1473 	return do_MakePath(base, error, buf, siz, path);
1474 }
1475 
FS_MakePath_VA(int base,int * error,const char * format,...)1476 char *FS_MakePath_VA (int base, int *error, const char *format, ...)
1477 {
1478 	va_list	argptr;
1479 	char	*p;
1480 
1481 	va_start (argptr, format);
1482 	p = do_MakePath_VA(base, error, get_fs_buffer(), FS_BUFFERLEN, format, argptr);
1483 	va_end (argptr);
1484 
1485 	return p;
1486 }
1487 
FS_MakePath_VABUF(int base,int * error,char * buf,size_t siz,const char * format,...)1488 char *FS_MakePath_VABUF (int base, int *error, char *buf, size_t siz, const char *format, ...)
1489 {
1490 	va_list	argptr;
1491 	char	*p;
1492 
1493 	va_start (argptr, format);
1494 	p = do_MakePath_VA(base, error, buf, siz, format, argptr);
1495 	va_end (argptr);
1496 
1497 	return p;
1498 }
1499 
FS_GetBasedir(void)1500 const char *FS_GetBasedir (void)
1501 {
1502 	return fs_basedir;
1503 }
1504 
FS_GetUserbase(void)1505 const char *FS_GetUserbase (void)
1506 {
1507 	return host_parms->userdir;
1508 }
1509 
FS_GetGamedir(void)1510 const char *FS_GetGamedir (void)
1511 {
1512 	return fs_gamedir;
1513 }
1514 
FS_GetUserdir(void)1515 const char *FS_GetUserdir (void)
1516 {
1517 	return fs_userdir;
1518 }
1519 
1520 /* The following FS_*() stdio replacements are necessary if one is
1521  * to perform non-sequential reads on files reopened on pak files
1522  * because we need the bookkeeping about file start/end positions.
1523  * Allocating and filling in the fshandle_t structure is the users'
1524  * responsibility when the file is initially opened. */
1525 
FS_fread(void * ptr,size_t size,size_t nmemb,fshandle_t * fh)1526 size_t FS_fread(void *ptr, size_t size, size_t nmemb, fshandle_t *fh)
1527 {
1528 	long byte_size;
1529 	long bytes_read;
1530 	size_t nmemb_read;
1531 
1532 	if (!fh) {
1533 		errno = EBADF;
1534 		return 0;
1535 	}
1536 	if (!ptr) {
1537 		errno = EFAULT;
1538 		return 0;
1539 	}
1540 	if (!size || !nmemb) {	/* no error, just zero bytes wanted */
1541 		errno = 0;
1542 		return 0;
1543 	}
1544 
1545 	byte_size = nmemb * size;
1546 	if (byte_size > fh->length - fh->pos)	/* just read to end */
1547 		byte_size = fh->length - fh->pos;
1548 	bytes_read = fread(ptr, 1, byte_size, fh->file);
1549 	fh->pos += bytes_read;
1550 
1551 	/* fread() must return the number of elements read,
1552 	 * not the total number of bytes. */
1553 	nmemb_read = bytes_read / size;
1554 	/* even if the last member is only read partially
1555 	 * it is counted as a whole in the return value. */
1556 	if (bytes_read % size)
1557 		nmemb_read++;
1558 
1559 	return nmemb_read;
1560 }
1561 
FS_fseek(fshandle_t * fh,long offset,int whence)1562 int FS_fseek(fshandle_t *fh, long offset, int whence)
1563 {
1564 /* I don't care about 64 bit off_t or fseeko() here.
1565  * the quake/hexen2 file system is 32 bits, anyway. */
1566 	int ret;
1567 
1568 	if (!fh) {
1569 		errno = EBADF;
1570 		return -1;
1571 	}
1572 
1573 	/* the relative file position shouldn't be smaller
1574 	 * than zero or bigger than the filesize. */
1575 	switch (whence)
1576 	{
1577 	case SEEK_SET:
1578 		break;
1579 	case SEEK_CUR:
1580 		offset += fh->pos;
1581 		break;
1582 	case SEEK_END:
1583 		offset = fh->length + offset;
1584 		break;
1585 	default:
1586 		errno = EINVAL;
1587 		return -1;
1588 	}
1589 
1590 	if (offset < 0) {
1591 		errno = EINVAL;
1592 		return -1;
1593 	}
1594 
1595 	if (offset > fh->length)	/* just seek to end */
1596 		offset = fh->length;
1597 
1598 	ret = fseek(fh->file, fh->start + offset, SEEK_SET);
1599 	if (ret < 0)
1600 		return ret;
1601 
1602 	fh->pos = offset;
1603 	return 0;
1604 }
1605 
FS_fclose(fshandle_t * fh)1606 int FS_fclose(fshandle_t *fh)
1607 {
1608 	if (!fh) {
1609 		errno = EBADF;
1610 		return -1;
1611 	}
1612 	return fclose(fh->file);
1613 }
1614 
FS_ftell(fshandle_t * fh)1615 long FS_ftell(fshandle_t *fh)
1616 {
1617 	if (!fh) {
1618 		errno = EBADF;
1619 		return -1;
1620 	}
1621 	return fh->pos;
1622 }
1623 
FS_rewind(fshandle_t * fh)1624 void FS_rewind(fshandle_t *fh)
1625 {
1626 	if (!fh) return;
1627 	clearerr(fh->file);
1628 	fseek(fh->file, fh->start, SEEK_SET);
1629 	fh->pos = 0;
1630 }
1631 
FS_feof(fshandle_t * fh)1632 int FS_feof(fshandle_t *fh)
1633 {
1634 	if (!fh) {
1635 		errno = EBADF;
1636 		return -1;
1637 	}
1638 	if (fh->pos >= fh->length)
1639 		return -1;
1640 	return 0;
1641 }
1642 
FS_ferror(fshandle_t * fh)1643 int FS_ferror(fshandle_t *fh)
1644 {
1645 	if (!fh) {
1646 		errno = EBADF;
1647 		return -1;
1648 	}
1649 	return ferror(fh->file);
1650 }
1651 
FS_fgetc(fshandle_t * fh)1652 int FS_fgetc(fshandle_t *fh)
1653 {
1654 	if (!fh) {
1655 		errno = EBADF;
1656 		return EOF;
1657 	}
1658 	if (fh->pos >= fh->length)
1659 		return EOF;
1660 	fh->pos += 1;
1661 	return fgetc(fh->file);
1662 }
1663 
FS_fgets(char * s,int size,fshandle_t * fh)1664 char *FS_fgets(char *s, int size, fshandle_t *fh)
1665 {
1666 	char *ret;
1667 
1668 	if (FS_feof(fh))
1669 		return NULL;
1670 
1671 	if (size > (fh->length - fh->pos) + 1)
1672 		size = (fh->length - fh->pos) + 1;
1673 
1674 	ret = fgets(s, size, fh->file);
1675 	fh->pos = ftell(fh->file) - fh->start;
1676 
1677 	return ret;
1678 }
1679 
FS_filelength(fshandle_t * fh)1680 long FS_filelength (fshandle_t *fh)
1681 {
1682 	if (!fh) {
1683 		errno = EBADF;
1684 		return -1;
1685 	}
1686 	return fh->length;
1687 }
1688 
1689