2009-04-22 20:04:28 +02:00

711 lines
14 KiB
C

/*
Copyright (C) 1997-2007 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )
http://www.zsnes.com
http://sourceforge.net/projects/zsnes
https://zsnes.bountysource.com
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef __UNIXSDL__
#include "gblhdr.h"
#include "linux/safelib.h"
#include <pwd.h>
#else
#ifdef __WIN32__
#include <io.h>
#include <direct.h>
#include "win/safelib.h"
#include "win/lib.h"
#else
#include "dos/lib.h"
#include <unistd.h>
#endif
#include <string.h>
#include <stdlib.h>
#endif
#include <errno.h>
#include "zpath.h"
#include "cfg.h"
#ifdef __WIN32__
#define mkdir_p(path) mkdir(path)
#else
#define mkdir_p(path) mkdir(path, mmode)
#endif
#ifdef __MSDOS__
char ZCfgFile[] = "zsnes.cfg";
#elif defined(__WIN32__)
char ZCfgFile[] = "zsnesw.cfg";
#else
char ZCfgFile[] = "zsnesl.cfg";
#endif
char *ZStartPath = 0, *ZCfgPath = 0, *ZSramPath = 0, *ZRomPath = 0;
char *ZSnapPath = 0, *ZSpcPath = 0;
char *ZCartName = 0, *ZSaveName = 0, *ZStateName = 0, *ZSaveST2Name = 0;
static bool ZStartAlloc = false, ZCfgAlloc = false, ZSramAlloc = false, ZRomAlloc = false;
static bool ZCartAlloc = false, ZSaveAlloc = false, ZStateAlloc = false, ZSaveST2Alloc = false;
#ifdef __UNIXSDL__
void cfgpath_ensure(const char *launch_command)
{
struct passwd *userinfo;
#ifdef ZCONF
const char *const zpath = ZCONF;
#else
#ifndef __MACOSX__
const char *const zpath = ".zsnes";
#else
const char *const zpath = "Library/Application Support/ZSNES";
#endif
#endif
if ((userinfo = getpwuid(getuid())))
{
ZCfgPath = malloc(PATH_SIZE);
}
else
{
puts("Error obtaining info about your user.");
}
if (ZCfgPath)
{
struct stat stat_buffer;
ZCfgAlloc = true;
strcpy(ZCfgPath, userinfo->pw_dir);
strcatslash(ZCfgPath);
strcat(ZCfgPath, zpath);
if (mkpath(ZCfgPath, 0755) && !stat(ZCfgPath, &stat_buffer) && S_ISDIR(stat_buffer.st_mode) && !access(ZCfgPath, W_OK))
{
strcatslash(ZCfgPath);
}
else
{
printf("Error creating: %s\n", ZCfgPath);
free(ZCfgPath);
ZCfgAlloc = false;
ZCfgPath = ZStartPath;
}
}
else
{
ZCfgPath = ZStartPath;
}
}
#else
void cfgpath_ensure(const char *launch_command)
{
ZCfgPath = malloc(PATH_SIZE);
if (ZCfgPath)
{
char *p = 0;
ZCfgAlloc = true;
if (isextension(launch_command, "exe"))
{
p = realpath(launch_command, ZCfgPath);
}
else
{
char buff[PATH_SIZE];
strcpy(buff, launch_command);
setextension(buff, "exe");
p = realpath(buff, ZCfgPath);
}
if (p)
{
strdirname(ZCfgPath);
strcatslash(ZCfgPath);
}
else
{
free(ZCfgPath);
ZCfgAlloc = false;
ZCfgPath = ZStartPath;
}
}
else
{
ZCfgPath = ZStartPath;
}
}
#endif
void SaveGameSpecificInput();
void deinit_paths()
{
//Save data that depends on paths before deinit of them
void SaveSramData();
void GUISaveVars();
strcpy(ROMPath, ZRomPath);
SaveSramData();
GUISaveVars();
SaveGameSpecificInput();
//Now deallocate the paths
if (ZStartAlloc && ZStartPath) { free(ZStartPath); }
if (ZCfgAlloc && ZCfgPath) { free(ZCfgPath); }
if (ZSramAlloc && ZSramPath) { free(ZSramPath); }
if (ZRomAlloc && ZRomPath) { free(ZRomPath); }
if (ZCartAlloc && ZCartName) { free(ZCartName); }
if (ZSaveAlloc && ZSaveName) { free(ZSaveName); }
if (ZStateAlloc && ZStateName) { free(ZStateName); }
if (ZSaveST2Alloc && ZSaveST2Name) { free(ZSaveST2Name); }
}
#define INIT_PATH_HELPER(x) if ((x##Path = malloc(PATH_SIZE))) { x##Alloc = true; } else { return(false); }
#define INIT_NAME_HELPER(x) if ((x##Name = malloc(NAME_SIZE))) { x##Alloc = true; *x##Name = 0; } else { return(false); }
bool init_paths(char *launch_command)
{
void GUIRestoreVars();
INIT_PATH_HELPER(ZStart);
INIT_PATH_HELPER(ZRom);
INIT_NAME_HELPER(ZCart);
INIT_NAME_HELPER(ZSave);
INIT_NAME_HELPER(ZState);
INIT_NAME_HELPER(ZSaveST2);
if (getcwd(ZStartPath, PATH_SIZE))
{
strcatslash(ZStartPath);
cfgpath_ensure(launch_command);
GUIRestoreVars();
if (*ROMPath && !access(strcutslash(ROMPath), DIR_R_ACCESS))
{
strcpy(ZRomPath, ROMPath);
}
else
{
strcpy(ZRomPath, ZStartPath);
}
strcatslash(ZRomPath);
init_save_paths();
#ifdef DEBUG
#ifndef __UNIXSDL__
fdreopen_dir(ZCfgPath, "stderr.txt", "w", STDERR_FILENO);
fdreopen_dir(ZCfgPath, "stdout.txt", "w", STDOUT_FILENO);
#endif
printf("ZStartPath: %s\n", ZStartPath);
printf("ZCfgPath: %s\n", ZCfgPath);
printf("ZRomPath: %s\n", ZRomPath);
printf("ZSramPath: %s\n", ZSramPath);
printf("ZSnapPath: %s\n", ZSnapPath);
printf("ZSpcPath: %s\n", ZSpcPath);
#endif
return(true);
}
return(false);
}
void init_save_paths()
{
if (*SRAMPath)
{
ZSramPath = SRAMPath;
}
else
{
#ifdef __UNIXSDL__
ZSramPath = ZCfgPath;
#else
ZSramPath = ZRomPath;
#endif
}
strcatslash(ZSramPath);
if (*SnapPath)
{
ZSnapPath = SnapPath;
}
else
{
ZSnapPath = ZSramPath;
}
strcatslash(ZSnapPath);
if (*SPCPath)
{
ZSpcPath = SPCPath;
}
else
{
ZSpcPath = ZSramPath;
}
strcatslash(ZSpcPath);
}
bool init_rom_path(char *path)
{
if (realpath_link(path, ZRomPath))
{
char *p;
SaveGameSpecificInput();
natify_slashes(ZRomPath);
p = strrchr(ZRomPath, DIR_SLASH_C);
strcpy(ZCartName, (p) ? p+1 : ZRomPath);
strcpy(ZSaveName, ZCartName);
strcpy(ZStateName, ZCartName);
setextension(ZStateName, "zst");
strdirname(ZRomPath);
strcatslash(ZRomPath);
#ifdef DEBUG
printf("ZRomPath: %s\n", ZRomPath);
printf("ZCartName: %s\n", ZCartName);
printf("ZStateName: %s\n", ZStateName);
#endif
return(true);
}
return(false);
}
char *strdupcat(const char *str1, const char *str2)
{
char *strnew = malloc(strlen(str1)+strlen(str2)+1);
if (strnew)
{
strcpy(strnew, str1);
strcat(strnew, str2);
return(strnew);
}
return(0);
}
#ifndef DEBUG
//This function is only for this file, and it uses an internal buffer, and is intended for path file merging
static const char *strdupcat_internal(const char *path, const char *file)
{
static char buffer_dir[PATH_SIZE*2];
if (!IS_ABSOLUTE(file))
{
strcpy(buffer_dir, path);
}
else
{
*buffer_dir = 0;
}
strcat(buffer_dir, file);
return(buffer_dir);
}
#define chdir_dir(path) chdir(path);
#else
static const char *strdupcat_internal(const char *path, const char *file, const char *func, const char *mode)
{
static char buffer_dir[PATH_SIZE*2];
if (!IS_ABSOLUTE(file))
{
strcpy(buffer_dir, path);
}
else
{
*buffer_dir = 0;
}
strcat(buffer_dir, file);
#ifndef NO_DEBUGGER
// maybe checking isendwin() would be better anyway, but only after we scrap
// the old debugger, because that won't work when not actually using curses
if (!debuggeron) {
#endif
if (mode)
{
printf("%s_%s: %s\n", func, mode, buffer_dir);
}
else
{
printf("%s: %s\n", func, buffer_dir);
}
#ifndef NO_DEBUGGER
}
#endif
return(buffer_dir);
}
//This is to keep the modeless functions working right
static const char *mode = 0;
static const char *mode_text = 0;
#define strdupcat_internal(x, y) strdupcat_internal(x, y, __func__, mode ? mode : mode_text)
int chdir_internal(const char *path, const char *func, const char *command)
{
printf("%s: %s: %s\n", func, path, command);
return(chdir(path));
}
#define chdir_dir(path) chdir_internal(path, __func__, command);
#endif
int access_dir(const char *path, const char *file, int amode)
{
#ifdef DEBUG
char mode_text[5];
strcpy(mode_text, "f");
if (amode & R_OK) { strcat(mode_text, "r"); }
if (amode & W_OK) { strcat(mode_text, "w"); }
if (amode & X_OK) { strcat(mode_text, "x"); }
#endif
return(access(strdupcat_internal(path, file), amode));
}
int stat_dir(const char *path, const char *file, struct stat *buf)
{
return(stat(strdupcat_internal(path, file), buf));
}
FILE *fopen_dir(const char *path, const char *file, const char *mode)
{
return(fopen(strdupcat_internal(path, file), mode));
}
gzFile gzopen_dir(const char *path, const char *file, const char *mode)
{
return(gzopen(strdupcat_internal(path, file), mode));
}
unzFile unzopen_dir(const char *path, const char *file)
{
return(unzOpen(strdupcat_internal(path, file)));
}
#ifndef NO_JMA
void load_jma_file_dir(const char *path, const char *file)
{
load_jma_file(strdupcat_internal(path, file));
}
#endif
int remove_dir(const char *path, const char *file)
{
return(remove(strdupcat_internal(path, file)));
}
int mkdir_dir(const char *path, const char *dir)
{
mode_t mmode = 0755;
return(mkdir_p(strdupcat_internal(path, dir)));
}
char *realpath_dir(const char *path, const char *file, char *buf)
{
#ifdef __UNIXSDL__
return(realpath_tilde(strdupcat_internal(path, file), buf));
#else
return(realpath(strdupcat_internal(path, file), buf));
#endif
}
#ifdef __MSDOS__
char *realpath_sfn_dir(const char *path, const char *file, char *buf)
{
return(realpath_sfn(strdupcat_internal(path, file), buf));
}
#endif
FILE *fdreopen_dir(const char *path, const char *file, const char *mode, int fd)
{
//Because DOSBox and Windows is stupid, we're implementing this manually;
FILE *fp = fopen(strdupcat_internal(path, file), mode);
if (fp)
{
dup2(fileno(fp), fd);
}
return(fp);
}
int system_dir(const char *path, const char *command)
{
int ret_val;
chdir_dir(path);
ret_val = system(command);
chdir(ZStartPath);
return(ret_val);
}
FILE *popen_dir(const char *path, char *command, const char *type)
{
FILE *ret_val;
chdir_dir(path);
ret_val = popen(command, type);
chdir(ZStartPath);
return(ret_val);
}
void natify_slashes(char *str)
{
while (*str)
{
if (*str == DIR_SLASH_C_OTHER)
{
*str = DIR_SLASH_C;
}
str++;
}
}
char *strcutslash(char *str)
{
char *last_char = str+(strlen(str)-1);
natify_slashes(str);
if (*last_char == DIR_SLASH_C)
{
*last_char = 0;
}
return(str);
}
char *strcatslash(char *str)
{
natify_slashes(str);
if (str[strlen(str)-1] != DIR_SLASH_C)
{
strcat(str, DIR_SLASH);
}
return(str);
}
void setextension(char *base, const char *ext)
{
char *p = strrchr(base, '.');
if(p)
{
strcpy(p+1, ext);
}
else
{
strcat(base, ".");
strcat(base, ext);
}
}
bool isextension(const char *fname, const char *ext)
{
size_t fname_len = strlen(fname),
ext_len = strlen(ext);
return((fname[fname_len-(ext_len+1)] == '.') && !strcasecmp(fname+fname_len-ext_len, ext));
}
void strdirname(char *str)
{
char *p;
size_t str_len = strlen(str);
natify_slashes(str);
do
{
str_len--;
} while (str_len && (str[str_len] == DIR_SLASH_C));
str[str_len+1] = 0;
p = strrchr(str, DIR_SLASH_C);
if (p > str)
{
*p = 0;
}
else if (p == str)
{
str[1] = 0;
}
}
void strbasename(char *str)
{
char *p;
natify_slashes(str);
if ((p = strrchr(str, DIR_SLASH_C)))
{
memmove(str, p+1, strlen(p));
}
}
static bool mkpath_help(char *path, char *element, mode_t mmode)
{
bool success = true;
if (*path)
{
char *p;
bool created;
while (*element == DIR_SLASH_C) { element++; }
if (*element)
{
p = strchr(element, DIR_SLASH_C);
if (p) { *p = 0; }
if ((created = !mkdir_p(path)) || (errno == EEXIST)) //Current path fragment created or already exists
{
if (p)
{
*p = DIR_SLASH_C;
if (!mkpath_help(path, p+1, mmode)) //If creation of next fragment fails
{
if (created)
{
*p = 0;
rmdir(path);
}
success = false;
}
}
}
else { success = false; }
}
}
return(success);
}
bool mkpath(const char *path, mode_t mode)
{
bool success = true;
if (path && *path)
{
char *p = strdup(path);
if (p)
{
natify_slashes(p);
success = mkpath_help(p, p, mode);
free(p);
}
else { success = false; }
}
return(success);
}
#ifdef __UNIXSDL__
//Like realpath(), but will return the last element as the link it is
char *realpath_link(const char *path, char *resolved_path)
{
char buffer[PATH_SIZE], *p, *base, *last_element;
strcpy(buffer, path);
natify_slashes(buffer);
p = strrchr(buffer, DIR_SLASH_C);
if (p)
{
*p = 0;
base = buffer;
last_element = p+1;
}
else
{
base = ".";
last_element = buffer;
}
p = realpath(base, resolved_path);
if (p)
{
strcatslash(resolved_path);
strcat(resolved_path, last_element);
return(resolved_path);
}
return(0);
}
//realpath() with ~ support
char *realpath_tilde(const char *path, char *resolved_path)
{
if (*path == '~')
{
char buffer[PATH_SIZE];
struct passwd *userinfo;
strcpy(buffer, "~");
path++;
if (isalpha(*path))
{
char *p = buffer+1;
while (isalnum(*path))
{
*p++ = *path++;
}
*p = 0;
if ((userinfo = getpwnam(buffer+1)))
{
strcpy(buffer, userinfo->pw_dir);
}
}
else
{
if ((userinfo = getpwuid(getuid())))
{
strcpy(buffer, userinfo->pw_dir);
}
}
strcat(buffer, path);
return(realpath(buffer, resolved_path));
}
return(realpath(path, resolved_path));
}
#endif
void psr_cfg_run(unsigned char (*psr_func)(const char *), const char *dir, const char *fname)
{
char *path = strdupcat(dir, fname);
if (path)
{
psr_func(path);
free(path);
}
else
{
psr_func(fname);
}
}