792 lines
15 KiB
C
792 lines
15 KiB
C
#define _CRT_SECURE_NO_WARNINGS
|
|
#include <windows.h>
|
|
#include <shlobj.h>
|
|
#include <direct.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <io.h>
|
|
|
|
#include "files.h"
|
|
|
|
static char *local_dir;
|
|
static char *global_dir;
|
|
|
|
/*
|
|
Sets the local and global directories used by the other functions.
|
|
Local = ~/.dir, where config and user-installed files are kept.
|
|
Global = installdir, where installed data is stored.
|
|
*/
|
|
int SetGameDirectories(const char *local, const char *global)
|
|
{
|
|
local_dir = _strdup(local);
|
|
global_dir = _strdup(global);
|
|
|
|
if( GetFileAttributes( local_dir ) == INVALID_FILE_ATTRIBUTES ) {
|
|
_mkdir( local_dir );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define DIR_SEPARATOR "\\"
|
|
|
|
static char *FixFilename(const char *filename, const char *prefix, int force)
|
|
{
|
|
char *f, *ptr;
|
|
size_t flen;
|
|
size_t plen;
|
|
|
|
plen = strlen(prefix) + 1;
|
|
flen = strlen(filename) + plen + 1;
|
|
|
|
f = (char *)malloc(flen);
|
|
strcpy(f, prefix);
|
|
strcat(f, DIR_SEPARATOR);
|
|
strcat(f, filename);
|
|
|
|
/* only the filename part needs to be modified */
|
|
ptr = &f[plen+1];
|
|
|
|
while (*ptr) {
|
|
if ((*ptr == '/') || (*ptr == '\\') || (*ptr == ':')) {
|
|
*ptr = DIR_SEPARATOR[0];
|
|
} else if (*ptr == '\r' || *ptr == '\n') {
|
|
*ptr = 0;
|
|
break;
|
|
} else {
|
|
if (force) {
|
|
*ptr = tolower(*ptr);
|
|
}
|
|
}
|
|
ptr++;
|
|
}
|
|
|
|
return f;
|
|
}
|
|
|
|
/*
|
|
Open a file of type type, with mode mode.
|
|
|
|
Mode can be:
|
|
#define FILEMODE_READONLY 0x01
|
|
#define FILEMODE_WRITEONLY 0x02
|
|
#define FILEMODE_READWRITE 0x04
|
|
#define FILEMODE_APPEND 0x08
|
|
Type is (mode = ReadOnly):
|
|
#define FILETYPE_PERM 0x08 // try the global dir only
|
|
#define FILETYPE_OPTIONAL 0x10 // try the global dir first, then try the local dir
|
|
#define FILETYPE_CONFIG 0x20 // try the local dir only
|
|
|
|
Type is (mode = WriteOnly or ReadWrite):
|
|
FILETYPE_PERM: error
|
|
FILETYPE_OPTIONAL: error
|
|
FILETYPE_CONFIG: try the local dir only
|
|
*/
|
|
FILE *OpenGameFile(const char *filename, int mode, int type)
|
|
{
|
|
char *rfilename;
|
|
char *openmode;
|
|
FILE *fp;
|
|
|
|
if ((type != FILETYPE_CONFIG) && (mode != FILEMODE_READONLY))
|
|
return NULL;
|
|
|
|
switch(mode) {
|
|
case FILEMODE_READONLY:
|
|
openmode = "rb";
|
|
break;
|
|
case FILEMODE_WRITEONLY:
|
|
openmode = "wb";
|
|
break;
|
|
case FILEMODE_READWRITE:
|
|
openmode = "w+";
|
|
break;
|
|
case FILEMODE_APPEND:
|
|
openmode = "ab";
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
if (type != FILETYPE_CONFIG) {
|
|
rfilename = FixFilename(filename, global_dir, 0);
|
|
|
|
fp = fopen(rfilename, openmode);
|
|
|
|
free(rfilename);
|
|
|
|
if (fp != NULL) {
|
|
return fp;
|
|
}
|
|
|
|
rfilename = FixFilename(filename, global_dir, 1);
|
|
|
|
fp = fopen(rfilename, openmode);
|
|
|
|
free(rfilename);
|
|
|
|
if (fp != NULL) {
|
|
return fp;
|
|
}
|
|
}
|
|
|
|
if (type != FILETYPE_PERM) {
|
|
rfilename = FixFilename(filename, local_dir, 0);
|
|
|
|
fp = fopen(rfilename, openmode);
|
|
|
|
free(rfilename);
|
|
|
|
if (fp != NULL) {
|
|
return fp;
|
|
}
|
|
|
|
rfilename = FixFilename(filename, local_dir, 1);
|
|
|
|
fp = fopen(rfilename, openmode);
|
|
|
|
free(rfilename);
|
|
|
|
return fp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int CloseGameFile(FILE *pfd)
|
|
{
|
|
return fclose(pfd);
|
|
}
|
|
|
|
/*
|
|
Get the filesystem attributes of a file
|
|
|
|
#define FILEATTR_DIRECTORY 0x0100
|
|
#define FILEATTR_READABLE 0x0200
|
|
#define FILEATTR_WRITABLE 0x0400
|
|
|
|
Error or can't access it: return value of 0 (What is the game going to do about it anyway?)
|
|
*/
|
|
static int GetFA(const char *filename)
|
|
{
|
|
struct stat buf;
|
|
int attr;
|
|
|
|
attr = 0;
|
|
if (stat(filename, &buf) == 0) {
|
|
if (buf.st_mode & _S_IFDIR) {
|
|
attr |= FILEATTR_DIRECTORY;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IREAD) {
|
|
attr |= FILEATTR_READABLE;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IWRITE) {
|
|
attr |= FILEATTR_WRITABLE;
|
|
}
|
|
}
|
|
|
|
return attr;
|
|
}
|
|
|
|
static time_t GetTS(const char *filename)
|
|
{
|
|
struct stat buf;
|
|
|
|
if (stat(filename, &buf) == 0) {
|
|
return buf.st_mtime;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int GetGameFileAttributes(const char *filename, int type)
|
|
{
|
|
struct stat buf;
|
|
char *rfilename;
|
|
int attr;
|
|
|
|
attr = 0;
|
|
if (type != FILETYPE_CONFIG) {
|
|
rfilename = FixFilename(filename, global_dir, 0);
|
|
|
|
if (stat(rfilename, &buf) == 0) {
|
|
if (buf.st_mode & _S_IFDIR) {
|
|
attr |= FILEATTR_DIRECTORY;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IREAD) {
|
|
attr |= FILEATTR_READABLE;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IWRITE) {
|
|
attr |= FILEATTR_WRITABLE;
|
|
}
|
|
|
|
free(rfilename);
|
|
|
|
return attr;
|
|
}
|
|
|
|
free(rfilename);
|
|
|
|
rfilename = FixFilename(filename, global_dir, 1);
|
|
|
|
if (stat(rfilename, &buf) == 0) {
|
|
if (buf.st_mode & _S_IFDIR) {
|
|
attr |= FILEATTR_DIRECTORY;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IREAD) {
|
|
attr |= FILEATTR_READABLE;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IWRITE) {
|
|
attr |= FILEATTR_WRITABLE;
|
|
}
|
|
|
|
free(rfilename);
|
|
|
|
return attr;
|
|
}
|
|
|
|
free(rfilename);
|
|
}
|
|
|
|
if (type != FILETYPE_PERM) {
|
|
rfilename = FixFilename(filename, local_dir, 0);
|
|
|
|
if (stat(rfilename, &buf) == 0) {
|
|
if (buf.st_mode & _S_IFDIR) {
|
|
attr |= FILEATTR_DIRECTORY;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IREAD) {
|
|
attr |= FILEATTR_READABLE;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IWRITE) {
|
|
attr |= FILEATTR_WRITABLE;
|
|
}
|
|
|
|
free(rfilename);
|
|
|
|
return attr;
|
|
}
|
|
|
|
free(rfilename);
|
|
|
|
rfilename = FixFilename(filename, local_dir, 1);
|
|
|
|
if (stat(rfilename, &buf) == 0) {
|
|
if (buf.st_mode & _S_IFDIR) {
|
|
attr |= FILEATTR_DIRECTORY;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IREAD) {
|
|
attr |= FILEATTR_READABLE;
|
|
}
|
|
|
|
if (buf.st_mode & _S_IWRITE) {
|
|
attr |= FILEATTR_WRITABLE;
|
|
}
|
|
|
|
free(rfilename);
|
|
|
|
return attr;
|
|
}
|
|
|
|
free(rfilename);
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Delete a file: local dir only
|
|
*/
|
|
int DeleteGameFile(const char *filename)
|
|
{
|
|
char *rfilename;
|
|
int ret;
|
|
|
|
rfilename = FixFilename(filename, local_dir, 0);
|
|
ret = _unlink(rfilename);
|
|
free(rfilename);
|
|
|
|
if (ret == -1) {
|
|
rfilename = FixFilename(filename, local_dir, 1);
|
|
ret = _unlink(rfilename);
|
|
free(rfilename);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
Create a directory: local dir only
|
|
|
|
TODO: maybe also mkdir parent directories, if they do not exist?
|
|
*/
|
|
int CreateGameDirectory(const char *dirname)
|
|
{
|
|
char *rfilename;
|
|
int ret;
|
|
|
|
rfilename = FixFilename(dirname, local_dir, 0);
|
|
ret = _mkdir(rfilename);
|
|
free(rfilename);
|
|
|
|
if (ret == -1) {
|
|
rfilename = FixFilename(dirname, local_dir, 1);
|
|
ret = _mkdir(rfilename);
|
|
free(rfilename);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* This struct is private. */
|
|
typedef struct GameDirectory
|
|
{
|
|
intptr_t localdir; /* directory opened with _findfirst */
|
|
intptr_t globaldir;
|
|
|
|
struct _finddata_t tmplocalfinddata;
|
|
struct _finddata_t localfinddata;
|
|
struct _finddata_t tmpglobalfinddata;
|
|
struct _finddata_t globalfinddata;
|
|
|
|
char *localdirname;
|
|
char *globaldirname;
|
|
|
|
GameDirectoryFile tmp; /* Temp space */
|
|
} GameDirectory;
|
|
|
|
/*
|
|
"Open" a directory dirname, with type type
|
|
Returns a pointer to a directory datatype
|
|
|
|
Pattern is the pattern to match
|
|
*/
|
|
void *OpenGameDirectory(const char *dirname, const char *pattern, int type)
|
|
{
|
|
char* localdirname;
|
|
char* globaldirname;
|
|
char* filespec;
|
|
|
|
intptr_t localdir;
|
|
intptr_t globaldir;
|
|
GameDirectory *gd;
|
|
|
|
gd = (GameDirectory *)malloc(sizeof(GameDirectory));
|
|
memset( gd, 0, sizeof(GameDirectory) );
|
|
|
|
globaldir = -1;
|
|
globaldirname = NULL;
|
|
if (type != FILETYPE_CONFIG) {
|
|
globaldirname = FixFilename(dirname, global_dir, 0);
|
|
|
|
filespec = (char*) malloc(strlen(globaldirname)+1+strlen(pattern)+1);
|
|
strcpy( filespec, globaldirname );
|
|
strcat( filespec, DIR_SEPARATOR );
|
|
strcat( filespec, pattern );
|
|
|
|
globaldir = _findfirst(filespec, &gd->tmpglobalfinddata);
|
|
free(filespec);
|
|
|
|
if (globaldir == -1L) {
|
|
free(globaldirname);
|
|
|
|
globaldirname = FixFilename(dirname, global_dir, 1);
|
|
|
|
filespec = (char*) malloc(strlen(globaldirname)+1+strlen(pattern)+1);
|
|
strcpy( filespec, globaldirname );
|
|
strcat( filespec, DIR_SEPARATOR );
|
|
strcat( filespec, pattern );
|
|
|
|
globaldir = _findfirst(filespec, &gd->tmpglobalfinddata);
|
|
free(filespec);
|
|
|
|
if (globaldir == -1L) {
|
|
free(globaldirname);
|
|
globaldirname = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
localdir = -1;
|
|
localdirname = NULL;
|
|
if (type != FILETYPE_PERM) {
|
|
localdirname = FixFilename(dirname, local_dir, 0);
|
|
|
|
filespec = (char*) malloc(strlen(localdirname)+1+strlen(pattern)+1);
|
|
strcpy( filespec, localdirname );
|
|
strcat( filespec, DIR_SEPARATOR );
|
|
strcat( filespec, pattern );
|
|
|
|
localdir = _findfirst(filespec, &gd->tmplocalfinddata);
|
|
free(filespec);
|
|
|
|
if (localdir == -1L) {
|
|
free(localdirname);
|
|
|
|
localdirname = FixFilename(dirname, local_dir, 1);
|
|
|
|
filespec = (char*) malloc(strlen(localdirname)+1+strlen(pattern)+1);
|
|
strcpy( filespec, localdirname );
|
|
strcat( filespec, DIR_SEPARATOR );
|
|
strcat( filespec, pattern );
|
|
|
|
localdir = _findfirst(filespec, &gd->tmplocalfinddata);
|
|
free( filespec );
|
|
|
|
if (localdir == -1L) {
|
|
free(localdirname);
|
|
localdirname = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (localdir == -1L && globaldir == -1L) {
|
|
free( gd );
|
|
return NULL;
|
|
}
|
|
|
|
gd->localdir = localdir;
|
|
gd->globaldir = globaldir;
|
|
|
|
gd->localdirname = localdirname;
|
|
gd->globaldirname = globaldirname;
|
|
|
|
return gd;
|
|
}
|
|
|
|
/*
|
|
This struct is public.
|
|
|
|
typedef struct GameDirectoryFile
|
|
{
|
|
char *filename;
|
|
int attr;
|
|
} GameDirectoryFile;
|
|
*/
|
|
|
|
/*
|
|
Returns the next match of pattern with the contents of dir
|
|
|
|
f is the current file
|
|
*/
|
|
GameDirectoryFile *ScanGameDirectory(void *dir)
|
|
{
|
|
char *ptr;
|
|
GameDirectory *directory;
|
|
|
|
directory = (GameDirectory *)dir;
|
|
|
|
if (directory->globaldir != -1L) {
|
|
directory->globalfinddata = directory->tmpglobalfinddata;
|
|
|
|
ptr = FixFilename(directory->globalfinddata.name, directory->globaldirname, 0);
|
|
directory->tmp.attr = GetFA(ptr);
|
|
directory->tmp.timestamp = GetTS(ptr);
|
|
free(ptr);
|
|
|
|
directory->tmp.filename = &directory->globalfinddata.name[0];
|
|
|
|
if( _findnext( directory->globaldir, &directory->tmpglobalfinddata ) == -1 ) {
|
|
_findclose(directory->globaldir);
|
|
free(directory->globaldirname);
|
|
|
|
directory->globaldir = -1L;
|
|
directory->globaldirname = NULL;
|
|
}
|
|
|
|
return &directory->tmp;
|
|
}
|
|
|
|
if (directory->localdir != -1L) {
|
|
directory->localfinddata = directory->tmplocalfinddata;
|
|
|
|
ptr = FixFilename(directory->localfinddata.name, directory->localdirname, 0);
|
|
directory->tmp.attr = GetFA(ptr);
|
|
directory->tmp.timestamp = GetTS(ptr);
|
|
free(ptr);
|
|
|
|
directory->tmp.filename = &directory->localfinddata.name[0];
|
|
|
|
if( _findnext( directory->localdir, &directory->tmplocalfinddata ) == -1 ) {
|
|
_findclose(directory->localdir);
|
|
free(directory->localdirname);
|
|
|
|
directory->localdir = -1L;
|
|
directory->localdirname = NULL;
|
|
}
|
|
|
|
return &directory->tmp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
Close directory
|
|
*/
|
|
int CloseGameDirectory(void *dir)
|
|
{
|
|
GameDirectory *directory = (GameDirectory *)dir;
|
|
|
|
if (directory != NULL) {
|
|
|
|
if (directory->localdirname != NULL) {
|
|
free(directory->localdirname);
|
|
}
|
|
if (directory->globaldirname != NULL) {
|
|
free(directory->globaldirname);
|
|
}
|
|
if (directory->localdir != -1L) {
|
|
_findclose(directory->localdir);
|
|
}
|
|
if (directory->globaldir != -1L) {
|
|
_findclose(directory->globaldir);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
Game-specific helper function.
|
|
*/
|
|
static int try_game_directory(const char *dir, const char *file)
|
|
{
|
|
char tmppath[MAX_PATH];
|
|
DWORD retr;
|
|
|
|
strncpy(tmppath, dir, MAX_PATH-32);
|
|
tmppath[MAX_PATH-32] = 0;
|
|
strcat(tmppath, file);
|
|
|
|
retr = GetFileAttributes(tmppath);
|
|
|
|
if( retr == INVALID_FILE_ATTRIBUTES ) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
TODO - expand this check to check for read access
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
Game-specific helper function.
|
|
*/
|
|
static int check_game_directory(const char *dir)
|
|
{
|
|
if (!dir || !*dir) {
|
|
return 0;
|
|
}
|
|
|
|
if (!try_game_directory(dir, "\\avp_huds")) {
|
|
return 0;
|
|
}
|
|
|
|
if (!try_game_directory(dir, "\\avp_huds\\alien.rif")) {
|
|
return 0;
|
|
}
|
|
|
|
if (!try_game_directory(dir, "\\avp_rifs")) {
|
|
return 0;
|
|
}
|
|
|
|
if (!try_game_directory(dir, "\\avp_rifs\\temple.rif")) {
|
|
return 0;
|
|
}
|
|
|
|
if (!try_game_directory(dir, "\\fastfile")) {
|
|
return 0;
|
|
}
|
|
|
|
if (!try_game_directory(dir, "\\fastfile\\ffinfo.txt")) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static char* GetLocalDirectory(void)
|
|
{
|
|
char folderPath[2 * MAX_PATH + 10];
|
|
char* localdir;
|
|
|
|
const char* homedrive;
|
|
const char* homepath;
|
|
char* homedir;
|
|
|
|
homedir = NULL;
|
|
|
|
/*
|
|
TODO - should check that the directory is actually usable.
|
|
*/
|
|
|
|
/*
|
|
1. Check registry (not currently implemented)
|
|
*/
|
|
|
|
/*
|
|
2. CSIDL_LOCAL_APPDATA with SHGetFolderPath
|
|
*/
|
|
if( homedir == NULL ) {
|
|
if( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_LOCAL_APPDATA,
|
|
NULL, SHGFP_TYPE_CURRENT, &folderPath[0] ) ) ) {
|
|
|
|
homedir = _strdup( folderPath );
|
|
}
|
|
}
|
|
|
|
/*
|
|
3. CSIDL_APPDATA with SHGetFolderPath
|
|
*/
|
|
if( homedir == NULL ) {
|
|
if( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_APPDATA,
|
|
NULL, SHGFP_TYPE_CURRENT, &folderPath[0] ) ) ) {
|
|
|
|
homedir = _strdup( folderPath );
|
|
}
|
|
}
|
|
|
|
/*
|
|
4. HOMEDRIVE+HOMEPATH
|
|
*/
|
|
|
|
if( homedir == NULL ) {
|
|
homedrive = getenv("HOMEDRIVE");
|
|
homepath = getenv("HOMEPATH");
|
|
|
|
if( homedrive == NULL ) {
|
|
homedrive = "";
|
|
}
|
|
|
|
if( homepath != NULL ) {
|
|
|
|
homedir = (char*)malloc(strlen(homedrive)+strlen(homepath)+1);
|
|
|
|
strcpy(homedir, homedrive);
|
|
strcat(homedir, homepath);
|
|
}
|
|
}
|
|
|
|
/*
|
|
5. HOME
|
|
|
|
*/
|
|
if( homedir == NULL ) {
|
|
homepath = getenv("HOME");
|
|
|
|
if( homepath != NULL ) {
|
|
homedir = _strdup(homepath);
|
|
}
|
|
}
|
|
|
|
/*
|
|
6. CWD
|
|
*/
|
|
if( homedir == NULL ) {
|
|
homedir = _strdup(".");
|
|
}
|
|
|
|
localdir = (char*)malloc(strlen(homedir) + 10);
|
|
strcpy(localdir, homedir);
|
|
strcat(localdir, "\\AvPLinux"); // temp name, maybe
|
|
|
|
free(homedir);
|
|
|
|
return localdir;
|
|
}
|
|
|
|
static const char* GetGlobalDirectory(const char* argv0)
|
|
{
|
|
char* gamedir;
|
|
char* tmp;
|
|
|
|
/*
|
|
1. $AVP_DATA overrides all
|
|
2. Registry Setting
|
|
3. executable path from argv[0]
|
|
4. current directory
|
|
*/
|
|
|
|
/* 1. $AVP_DATA */
|
|
gamedir = getenv("AVP_DATA");
|
|
|
|
/* $AVP_DATA overrides all, so no check */
|
|
|
|
/* 2. Registry Setting */
|
|
/* TODO */
|
|
|
|
if (gamedir == NULL) {
|
|
/* 3. executable path from argv[0] */
|
|
tmp = _strdup(argv0);
|
|
|
|
if (tmp == NULL) {
|
|
/* ... */
|
|
fprintf(stderr, "GetGlobalDirectory failure\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
gamedir = strrchr(tmp, DIR_SEPARATOR[0]);
|
|
|
|
if (gamedir != NULL) {
|
|
*gamedir = 0;
|
|
gamedir = tmp;
|
|
|
|
if (!check_game_directory(gamedir)) {
|
|
gamedir = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 4. current directory */
|
|
return _strdup(".");
|
|
}
|
|
|
|
/*
|
|
Game-specific initialization
|
|
*/
|
|
extern char const *SecondTex_Directory;
|
|
extern char const *SecondSoundDir;
|
|
|
|
void InitGameDirectories(char *argv0)
|
|
{
|
|
const char* localdir;
|
|
const char* globaldir;
|
|
|
|
SecondTex_Directory = "graphics\\";
|
|
SecondSoundDir = "sound\\";
|
|
|
|
localdir = GetLocalDirectory();
|
|
globaldir = GetGlobalDirectory(argv0);
|
|
|
|
assert(localdir != NULL);
|
|
assert(globaldir != NULL);
|
|
|
|
/* last chance sanity check */
|
|
if (!check_game_directory(globaldir)) {
|
|
fprintf(stderr, "Unable to find the AvP gamedata.\n");
|
|
fprintf(stderr, "The directory last examined was: %s\n", globaldir);
|
|
fprintf(stderr, "Has the game been installed and\n");
|
|
fprintf(stderr, "are all game files lowercase?\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
SetGameDirectories(localdir, globaldir);
|
|
}
|