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