1 #define _CRT_SECURE_NO_WARNINGS
2 #include <windows.h>
3 #include <shlobj.h>
4 #include <direct.h>
5 #include <assert.h>
6 #include <string.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <io.h>
10 
11 #include "files.h"
12 
13 static char *local_dir;
14 static char *global_dir;
15 
16 /*
17 Sets the local and global directories used by the other functions.
18 Local = ~/.dir, where config and user-installed files are kept.
19 Global = installdir, where installed data is stored.
20 */
SetGameDirectories(const char * local,const char * global)21 int SetGameDirectories(const char *local, const char *global)
22 {
23 	local_dir = _strdup(local);
24 	global_dir = _strdup(global);
25 
26 	if( GetFileAttributes( local_dir ) == INVALID_FILE_ATTRIBUTES ) {
27 		_mkdir( local_dir );
28 	}
29 
30 	return 0;
31 }
32 
33 
34 #define DIR_SEPARATOR	"\\"
35 
FixFilename(const char * filename,const char * prefix,int force)36 static char *FixFilename(const char *filename, const char *prefix, int force)
37 {
38 	char *f, *ptr;
39 	size_t flen;
40 	size_t plen;
41 
42 	plen = strlen(prefix) + 1;
43 	flen = strlen(filename) + plen + 1;
44 
45 	f = (char *)malloc(flen);
46 	strcpy(f, prefix);
47 	strcat(f, DIR_SEPARATOR);
48 	strcat(f, filename);
49 
50 	/* only the filename part needs to be modified */
51 	ptr = &f[plen+1];
52 
53 	while (*ptr) {
54 		if ((*ptr == '/') || (*ptr == '\\') || (*ptr == ':')) {
55 			*ptr = DIR_SEPARATOR[0];
56 		} else if (*ptr == '\r' || *ptr == '\n') {
57 			*ptr = 0;
58 			break;
59 		} else {
60 			if (force) {
61 				*ptr = tolower(*ptr);
62 			}
63 		}
64 		ptr++;
65 	}
66 
67 	return f;
68 }
69 
70 /*
71 Open a file of type type, with mode mode.
72 
73 Mode can be:
74 #define	FILEMODE_READONLY	0x01
75 #define	FILEMODE_WRITEONLY	0x02
76 #define	FILEMODE_READWRITE	0x04
77 #define FILEMODE_APPEND		0x08
78 Type is (mode = ReadOnly):
79 #define	FILETYPE_PERM		0x08 // try the global dir only
80 #define	FILETYPE_OPTIONAL	0x10 // try the global dir first, then try the local dir
81 #define	FILETYPE_CONFIG		0x20 // try the local dir only
82 
83 Type is (mode = WriteOnly or ReadWrite):
84 FILETYPE_PERM: error
85 FILETYPE_OPTIONAL: error
86 FILETYPE_CONFIG: try the local dir only
87 */
OpenGameFile(const char * filename,int mode,int type)88 FILE *OpenGameFile(const char *filename, int mode, int type)
89 {
90 	char *rfilename;
91 	char *openmode;
92 	FILE *fp;
93 
94 	if ((type != FILETYPE_CONFIG) && (mode != FILEMODE_READONLY))
95 		return NULL;
96 
97 	switch(mode) {
98 		case FILEMODE_READONLY:
99 			openmode = "rb";
100 			break;
101 		case FILEMODE_WRITEONLY:
102 			openmode = "wb";
103 			break;
104 		case FILEMODE_READWRITE:
105 			openmode = "w+";
106 			break;
107 		case FILEMODE_APPEND:
108 			openmode = "ab";
109 			break;
110 		default:
111 			return NULL;
112 	}
113 
114 	if (type != FILETYPE_CONFIG) {
115 		rfilename = FixFilename(filename, global_dir, 0);
116 
117 		fp = fopen(rfilename, openmode);
118 
119 		free(rfilename);
120 
121 		if (fp != NULL) {
122 			return fp;
123 		}
124 
125 		rfilename = FixFilename(filename, global_dir, 1);
126 
127 		fp = fopen(rfilename, openmode);
128 
129 		free(rfilename);
130 
131 		if (fp != NULL) {
132 			return fp;
133 		}
134 	}
135 
136 	if (type != FILETYPE_PERM) {
137 		rfilename = FixFilename(filename, local_dir, 0);
138 
139 		fp = fopen(rfilename, openmode);
140 
141 		free(rfilename);
142 
143 		if (fp != NULL) {
144 			return fp;
145 		}
146 
147 		rfilename = FixFilename(filename, local_dir, 1);
148 
149 		fp = fopen(rfilename, openmode);
150 
151 		free(rfilename);
152 
153 		return fp;
154 	}
155 
156 	return NULL;
157 }
158 
CloseGameFile(FILE * pfd)159 int CloseGameFile(FILE *pfd)
160 {
161 	return fclose(pfd);
162 }
163 
164 /*
165 Get the filesystem attributes of a file
166 
167 #define	FILEATTR_DIRECTORY	0x0100
168 #define FILEATTR_READABLE	0x0200
169 #define FILEATTR_WRITABLE	0x0400
170 
171 Error or can't access it: return value of 0 (What is the game going to do about it anyway?)
172 */
GetFA(const char * filename)173 static int GetFA(const char *filename)
174 {
175 	struct stat buf;
176 	int attr;
177 
178 	attr = 0;
179 	if (stat(filename, &buf) == 0) {
180 		if (buf.st_mode & _S_IFDIR) {
181 			attr |= FILEATTR_DIRECTORY;
182 		}
183 
184 		if (buf.st_mode & _S_IREAD) {
185 			attr |= FILEATTR_READABLE;
186 		}
187 
188 		if (buf.st_mode & _S_IWRITE) {
189 			attr |= FILEATTR_WRITABLE;
190 		}
191 	}
192 
193 	return attr;
194 }
195 
GetTS(const char * filename)196 static time_t GetTS(const char *filename)
197 {
198 	struct stat buf;
199 
200 	if (stat(filename, &buf) == 0) {
201 		return buf.st_mtime;
202 	}
203 
204 	return 0;
205 }
206 
GetGameFileAttributes(const char * filename,int type)207 int GetGameFileAttributes(const char *filename, int type)
208 {
209 	struct stat buf;
210 	char *rfilename;
211 	int attr;
212 
213 	attr = 0;
214 	if (type != FILETYPE_CONFIG) {
215 		rfilename = FixFilename(filename, global_dir, 0);
216 
217 		if (stat(rfilename, &buf) == 0) {
218 			if (buf.st_mode & _S_IFDIR) {
219 				attr |= FILEATTR_DIRECTORY;
220 			}
221 
222 			if (buf.st_mode & _S_IREAD) {
223 				attr |= FILEATTR_READABLE;
224 			}
225 
226 			if (buf.st_mode & _S_IWRITE) {
227 				attr |= FILEATTR_WRITABLE;
228 			}
229 
230 			free(rfilename);
231 
232 			return attr;
233 		}
234 
235 		free(rfilename);
236 
237 		rfilename = FixFilename(filename, global_dir, 1);
238 
239 		if (stat(rfilename, &buf) == 0) {
240 			if (buf.st_mode & _S_IFDIR) {
241 				attr |= FILEATTR_DIRECTORY;
242 			}
243 
244 			if (buf.st_mode & _S_IREAD) {
245 				attr |= FILEATTR_READABLE;
246 			}
247 
248 			if (buf.st_mode & _S_IWRITE) {
249 				attr |= FILEATTR_WRITABLE;
250 			}
251 
252 			free(rfilename);
253 
254 			return attr;
255 		}
256 
257 		free(rfilename);
258 	}
259 
260 	if (type != FILETYPE_PERM) {
261 		rfilename = FixFilename(filename, local_dir, 0);
262 
263 		if (stat(rfilename, &buf) == 0) {
264 			if (buf.st_mode & _S_IFDIR) {
265 				attr |= FILEATTR_DIRECTORY;
266 			}
267 
268 			if (buf.st_mode & _S_IREAD) {
269 				attr |= FILEATTR_READABLE;
270 			}
271 
272 			if (buf.st_mode & _S_IWRITE) {
273 				attr |= FILEATTR_WRITABLE;
274 			}
275 
276 			free(rfilename);
277 
278 			return attr;
279 		}
280 
281 		free(rfilename);
282 
283 		rfilename = FixFilename(filename, local_dir, 1);
284 
285 		if (stat(rfilename, &buf) == 0) {
286 			if (buf.st_mode & _S_IFDIR) {
287 				attr |= FILEATTR_DIRECTORY;
288 			}
289 
290 			if (buf.st_mode & _S_IREAD) {
291 				attr |= FILEATTR_READABLE;
292 			}
293 
294 			if (buf.st_mode & _S_IWRITE) {
295 				attr |= FILEATTR_WRITABLE;
296 			}
297 
298 			free(rfilename);
299 
300 			return attr;
301 		}
302 
303 		free(rfilename);
304 
305 	}
306 
307 	return 0;
308 }
309 
310 /*
311 Delete a file: local dir only
312 */
DeleteGameFile(const char * filename)313 int DeleteGameFile(const char *filename)
314 {
315 	char *rfilename;
316 	int ret;
317 
318 	rfilename = FixFilename(filename, local_dir, 0);
319 	ret = _unlink(rfilename);
320 	free(rfilename);
321 
322 	if (ret == -1) {
323 		rfilename = FixFilename(filename, local_dir, 1);
324 		ret = _unlink(rfilename);
325 		free(rfilename);
326 	}
327 
328 	return ret;
329 }
330 
331 /*
332 Create a directory: local dir only
333 
334 TODO: maybe also mkdir parent directories, if they do not exist?
335 */
CreateGameDirectory(const char * dirname)336 int CreateGameDirectory(const char *dirname)
337 {
338 	char *rfilename;
339 	int ret;
340 
341 	rfilename = FixFilename(dirname, local_dir, 0);
342 	ret = _mkdir(rfilename);
343 	free(rfilename);
344 
345 	if (ret == -1) {
346 		rfilename = FixFilename(dirname, local_dir, 1);
347 		ret = _mkdir(rfilename);
348 		free(rfilename);
349 	}
350 
351 	return ret;
352 }
353 
354 
355 /* This struct is private. */
356 typedef struct GameDirectory
357 {
358 	intptr_t localdir;		/* directory opened with _findfirst */
359 	intptr_t globaldir;
360 
361 	struct _finddata_t tmplocalfinddata;
362 	struct _finddata_t localfinddata;
363 	struct _finddata_t tmpglobalfinddata;
364 	struct _finddata_t globalfinddata;
365 
366 	char *localdirname;
367 	char *globaldirname;
368 
369 	GameDirectoryFile tmp;	/* Temp space */
370 } GameDirectory;
371 
372 /*
373 "Open" a directory dirname, with type type
374 Returns a pointer to a directory datatype
375 
376 Pattern is the pattern to match
377 */
OpenGameDirectory(const char * dirname,const char * pattern,int type)378 void *OpenGameDirectory(const char *dirname, const char *pattern, int type)
379 {
380 	char* localdirname;
381 	char* globaldirname;
382 	char* filespec;
383 
384 	intptr_t localdir;
385 	intptr_t globaldir;
386 	GameDirectory *gd;
387 
388 	gd = (GameDirectory *)malloc(sizeof(GameDirectory));
389 	memset( gd, 0, sizeof(GameDirectory) );
390 
391 	globaldir = -1;
392 	globaldirname = NULL;
393 	if (type != FILETYPE_CONFIG) {
394 		globaldirname = FixFilename(dirname, global_dir, 0);
395 
396 		filespec = (char*) malloc(strlen(globaldirname)+1+strlen(pattern)+1);
397 		strcpy( filespec, globaldirname );
398 		strcat( filespec, DIR_SEPARATOR );
399 		strcat( filespec, pattern );
400 
401 		globaldir = _findfirst(filespec, &gd->tmpglobalfinddata);
402 		free(filespec);
403 
404 		if (globaldir == -1L) {
405 			free(globaldirname);
406 
407 			globaldirname = FixFilename(dirname, global_dir, 1);
408 
409 			filespec = (char*) malloc(strlen(globaldirname)+1+strlen(pattern)+1);
410 			strcpy( filespec, globaldirname );
411 			strcat( filespec, DIR_SEPARATOR );
412 			strcat( filespec, pattern );
413 
414 			globaldir = _findfirst(filespec, &gd->tmpglobalfinddata);
415 			free(filespec);
416 
417 			if (globaldir == -1L) {
418 				free(globaldirname);
419 				globaldirname = NULL;
420 			}
421 		}
422 	}
423 
424 	localdir = -1;
425 	localdirname = NULL;
426 	if (type != FILETYPE_PERM) {
427 		localdirname = FixFilename(dirname, local_dir, 0);
428 
429 		filespec = (char*) malloc(strlen(localdirname)+1+strlen(pattern)+1);
430 		strcpy( filespec, localdirname );
431 		strcat( filespec, DIR_SEPARATOR );
432 		strcat( filespec, pattern );
433 
434 		localdir = _findfirst(filespec, &gd->tmplocalfinddata);
435 		free(filespec);
436 
437 		if (localdir == -1L) {
438 			free(localdirname);
439 
440 			localdirname = FixFilename(dirname, local_dir, 1);
441 
442 			filespec = (char*) malloc(strlen(localdirname)+1+strlen(pattern)+1);
443 			strcpy( filespec, localdirname );
444 			strcat( filespec, DIR_SEPARATOR );
445 			strcat( filespec, pattern );
446 
447 			localdir = _findfirst(filespec, &gd->tmplocalfinddata);
448 			free( filespec );
449 
450 			if (localdir == -1L) {
451 				free(localdirname);
452 				localdirname = NULL;
453 			}
454 		}
455 	}
456 
457 	if (localdir == -1L && globaldir == -1L) {
458 		free( gd );
459 		return NULL;
460 	}
461 
462 	gd->localdir = localdir;
463 	gd->globaldir = globaldir;
464 
465 	gd->localdirname = localdirname;
466 	gd->globaldirname = globaldirname;
467 
468 	return gd;
469 }
470 
471 /*
472 This struct is public.
473 
474 typedef struct GameDirectoryFile
475 {
476 	char *filename;
477 	int attr;
478 } GameDirectoryFile;
479 */
480 
481 /*
482 Returns the next match of pattern with the contents of dir
483 
484 f is the current file
485 */
ScanGameDirectory(void * dir)486 GameDirectoryFile *ScanGameDirectory(void *dir)
487 {
488 	char *ptr;
489 	GameDirectory *directory;
490 
491 	directory = (GameDirectory *)dir;
492 
493 	if (directory->globaldir != -1L) {
494 		directory->globalfinddata = directory->tmpglobalfinddata;
495 
496 		ptr = FixFilename(directory->globalfinddata.name, directory->globaldirname, 0);
497 		directory->tmp.attr = GetFA(ptr);
498 		directory->tmp.timestamp = GetTS(ptr);
499 		free(ptr);
500 
501 		directory->tmp.filename = &directory->globalfinddata.name[0];
502 
503 		if( _findnext( directory->globaldir, &directory->tmpglobalfinddata ) == -1 ) {
504 				_findclose(directory->globaldir);
505 				free(directory->globaldirname);
506 
507 				directory->globaldir = -1L;
508 				directory->globaldirname = NULL;
509 		}
510 
511 		return &directory->tmp;
512 	}
513 
514 	if (directory->localdir != -1L) {
515 		directory->localfinddata = directory->tmplocalfinddata;
516 
517 		ptr = FixFilename(directory->localfinddata.name, directory->localdirname, 0);
518 		directory->tmp.attr = GetFA(ptr);
519 		directory->tmp.timestamp = GetTS(ptr);
520 		free(ptr);
521 
522 		directory->tmp.filename = &directory->localfinddata.name[0];
523 
524 		if( _findnext( directory->localdir, &directory->tmplocalfinddata ) == -1 ) {
525 				_findclose(directory->localdir);
526 				free(directory->localdirname);
527 
528 				directory->localdir = -1L;
529 				directory->localdirname = NULL;
530 		}
531 
532 		return &directory->tmp;
533 	}
534 
535 	return NULL;
536 }
537 
538 /*
539 Close directory
540 */
CloseGameDirectory(void * dir)541 int CloseGameDirectory(void *dir)
542 {
543 	GameDirectory *directory = (GameDirectory *)dir;
544 
545 	if (directory != NULL) {
546 
547 		if (directory->localdirname != NULL) {
548 			free(directory->localdirname);
549 		}
550 		if (directory->globaldirname != NULL) {
551 			free(directory->globaldirname);
552 		}
553 		if (directory->localdir != -1L) {
554 			_findclose(directory->localdir);
555 		}
556 		if (directory->globaldir != -1L) {
557 			_findclose(directory->globaldir);
558 		}
559 
560 		return 0;
561 	}
562 	return -1;
563 }
564 
565 /*
566   Game-specific helper function.
567  */
try_game_directory(const char * dir,const char * file)568 static int try_game_directory(const char *dir, const char *file)
569 {
570 	char tmppath[MAX_PATH];
571 	DWORD retr;
572 
573 	strncpy(tmppath, dir, MAX_PATH-32);
574 	tmppath[MAX_PATH-32] = 0;
575 	strcat(tmppath, file);
576 
577 	retr = GetFileAttributes(tmppath);
578 
579 	if( retr == INVALID_FILE_ATTRIBUTES ) {
580 		return 0;
581 	}
582 
583 	/*
584 	  TODO - expand this check to check for read access
585      */
586 	return 1;
587 }
588 
589 /*
590   Game-specific helper function.
591  */
check_game_directory(const char * dir)592 static int check_game_directory(const char *dir)
593 {
594 	if (!dir || !*dir) {
595 		return 0;
596 	}
597 
598 	if (!try_game_directory(dir, "\\avp_huds")) {
599 		return 0;
600 	}
601 
602 	if (!try_game_directory(dir, "\\avp_huds\\alien.rif")) {
603 		return 0;
604 	}
605 
606 	if (!try_game_directory(dir, "\\avp_rifs")) {
607 		return 0;
608 	}
609 
610 	if (!try_game_directory(dir, "\\avp_rifs\\temple.rif")) {
611 		return 0;
612 	}
613 
614 	if (!try_game_directory(dir, "\\fastfile")) {
615 		return 0;
616 	}
617 
618 	if (!try_game_directory(dir, "\\fastfile\\ffinfo.txt")) {
619 		return 0;
620 	}
621 
622 	return 1;
623 }
624 
625 
GetLocalDirectory(void)626 static char* GetLocalDirectory(void)
627 {
628 	char folderPath[2 * MAX_PATH + 10];
629 	char* localdir;
630 
631 	const char* homedrive;
632 	const char* homepath;
633 	char* homedir;
634 
635 	homedir = NULL;
636 
637 	/*
638 	  TODO - should check that the directory is actually usable.
639      */
640 
641 	/*
642 	   1. Check registry (not currently implemented)
643 	 */
644 
645 	/*
646 	   2. CSIDL_LOCAL_APPDATA with SHGetFolderPath
647 	 */
648 	if( homedir == NULL ) {
649 		if( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_LOCAL_APPDATA,
650 			NULL, SHGFP_TYPE_CURRENT, &folderPath[0] ) ) ) {
651 
652 			homedir = _strdup( folderPath );
653 		}
654 	}
655 
656 	/*
657 	   3. CSIDL_APPDATA with SHGetFolderPath
658      */
659 	if( homedir == NULL ) {
660 		if( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_APPDATA,
661 			NULL, SHGFP_TYPE_CURRENT, &folderPath[0] ) ) ) {
662 
663 			homedir = _strdup( folderPath );
664 		}
665 	}
666 
667 	/*
668 	   4. HOMEDRIVE+HOMEPATH
669      */
670 
671 	if( homedir == NULL ) {
672 		homedrive = getenv("HOMEDRIVE");
673 		homepath  = getenv("HOMEPATH");
674 
675 		if( homedrive == NULL ) {
676 			homedrive = "";
677 		}
678 
679 		if( homepath != NULL ) {
680 
681 			homedir = (char*)malloc(strlen(homedrive)+strlen(homepath)+1);
682 
683 			strcpy(homedir, homedrive);
684 			strcat(homedir, homepath);
685 		}
686 	}
687 
688 	/*
689 	   5. HOME
690 
691      */
692 	if( homedir == NULL ) {
693 		homepath = getenv("HOME");
694 
695 		if( homepath != NULL ) {
696 			homedir = _strdup(homepath);
697 		}
698 	}
699 
700 	/*
701 	  6. CWD
702      */
703 	if( homedir == NULL ) {
704 		homedir = _strdup(".");
705 	}
706 
707 	localdir = (char*)malloc(strlen(homedir) + 10);
708 	strcpy(localdir, homedir);
709 	strcat(localdir, "\\AvPLinux"); // temp name, maybe
710 
711 	free(homedir);
712 
713 	return localdir;
714 }
715 
GetGlobalDirectory(const char * argv0)716 static const char* GetGlobalDirectory(const char* argv0)
717 {
718 	char* gamedir;
719 	char* tmp;
720 
721 	/*
722 	1. $AVP_DATA overrides all
723 	2. Registry Setting
724 	3. executable path from argv[0]
725 	4. current directory
726 	*/
727 
728 	/* 1. $AVP_DATA */
729 	gamedir = getenv("AVP_DATA");
730 
731 	/* $AVP_DATA overrides all, so no check */
732 
733 	/* 2. Registry Setting */
734 	/* TODO */
735 
736 	if (gamedir == NULL) {
737 		/* 3. executable path from argv[0] */
738 		tmp = _strdup(argv0);
739 
740 		if (tmp == NULL) {
741 			/* ... */
742 			fprintf(stderr, "GetGlobalDirectory failure\n");
743 			exit(EXIT_FAILURE);
744 		}
745 
746 		gamedir = strrchr(tmp, DIR_SEPARATOR[0]);
747 
748 		if (gamedir != NULL) {
749 			*gamedir = 0;
750 			gamedir = tmp;
751 
752 			if (!check_game_directory(gamedir)) {
753 				gamedir = NULL;
754 			}
755 		}
756 	}
757 
758 	/* 4. current directory */
759 	return _strdup(".");
760 }
761 
762 /*
763   Game-specific initialization
764  */
765 extern char const *SecondTex_Directory;
766 extern char const *SecondSoundDir;
767 
InitGameDirectories(char * argv0)768 void InitGameDirectories(char *argv0)
769 {
770 	const char* localdir;
771 	const char* globaldir;
772 
773 	SecondTex_Directory = "graphics\\";
774 	SecondSoundDir = "sound\\";
775 
776 	localdir  = GetLocalDirectory();
777 	globaldir = GetGlobalDirectory(argv0);
778 
779 	assert(localdir != NULL);
780 	assert(globaldir != NULL);
781 
782 	/* last chance sanity check */
783 	if (!check_game_directory(globaldir)) {
784 		fprintf(stderr, "Unable to find the AvP gamedata.\n");
785 		fprintf(stderr, "The directory last examined was: %s\n", globaldir);
786 		fprintf(stderr, "Has the game been installed and\n");
787 		fprintf(stderr, "are all game files lowercase?\n");
788 		exit(EXIT_FAILURE);
789 	}
790 
791 	SetGameDirectories(localdir, globaldir);
792 }
793