--- a/src/fileio.cpp Wed Jun 13 12:05:56 2007 +0000
+++ b/src/fileio.cpp Tue Jun 19 07:21:01 2007 +0000
@@ -11,7 +11,9 @@
#include "variables.h"
#include "debug.h"
#include "fios.h"
-#ifndef WIN32
+#ifdef WIN32
+#include <windows.h>
+#else
#include <pwd.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -28,11 +30,12 @@
byte *buffer, *buffer_end; ///< position pointer in local buffer and last valid byte of buffer
uint32 pos; ///< current (system) position in file
FILE *cur_fh; ///< current file handle
+ const char *filename; ///< current filename
FILE *handles[MAX_HANDLES]; ///< array of file handles we can have open
byte buffer_start[FIO_BUFFER_SIZE]; ///< local buffer when read from file
+ const char *filenames[MAX_HANDLES]; ///< array of filenames we (should) have open
#if defined(LIMITED_FDS)
uint open_handles; ///< current amount of open handles
- const char *filename[MAX_HANDLES]; ///< array of filenames we (should) have open
uint usage_count[MAX_HANDLES]; ///< count how many times this file has been opened
#endif /* LIMITED_FDS */
};
@@ -45,6 +48,11 @@
return _fio.pos + (_fio.buffer - _fio.buffer_start) - FIO_BUFFER_SIZE;
}
+const char *FioGetFilename()
+{
+ return _fio.filename;
+}
+
void FioSeekTo(uint32 pos, int mode)
{
if (mode == SEEK_CUR) pos += FioGetPos();
@@ -76,6 +84,7 @@
f = _fio.handles[pos >> 24];
assert(f != NULL);
_fio.cur_fh = f;
+ _fio.filename = _fio.filenames[pos >> 24];
FioSeekTo(GB(pos, 0, 24), SEEK_SET);
}
@@ -174,60 +183,130 @@
FioCloseFile(slot); // if file was opened before, close it
_fio.handles[slot] = f;
+ _fio.filenames[slot] = filename;
#if defined(LIMITED_FDS)
- _fio.filename[slot] = filename;
_fio.usage_count[slot] = 0;
_fio.open_handles++;
#endif /* LIMITED_FDS */
FioSeekToFile(slot << 24);
}
+const char *_subdirs[NUM_SUBDIRS] = {
+ "",
+ "save" PATHSEP,
+ "save" PATHSEP "autosave" PATHSEP,
+ "scenario" PATHSEP,
+ "scenario" PATHSEP "heightmap" PATHSEP,
+ "gm" PATHSEP,
+ "data" PATHSEP,
+ "lang" PATHSEP
+};
+
+const char *_searchpaths[NUM_SEARCHPATHS];
+
/**
* Check whether the given file exists
* @param filename the file to try for existance
+ * @param subdir the subdirectory to look in
* @return true if and only if the file can be opened
*/
-bool FioCheckFileExists(const char *filename)
+bool FioCheckFileExists(const char *filename, Subdirectory subdir)
{
- FILE *f = FioFOpenFile(filename);
+ FILE *f = FioFOpenFile(filename, "rb", subdir);
if (f == NULL) return false;
fclose(f);
return true;
}
-/**
- * Opens the file with the given name
- * @param filename the file to open (in either data_dir or second_data_dir)
- * @return the opened file or NULL when it failed.
- */
-FILE *FioFOpenFile(const char *filename)
+char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
{
- FILE *f;
- char buf[MAX_PATH];
+ assert(subdir < NUM_SUBDIRS);
+ assert(sp < NUM_SEARCHPATHS);
- if (filename[0] == PATHSEPCHAR || filename[1] == ':') {
- ttd_strlcpy(buf, filename, lengthof(buf));
- } else {
- snprintf(buf, lengthof(buf), "%s%s", _paths.data_dir, filename);
+ snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
+ return buf;
+}
+
+char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
+{
+ Searchpath sp;
+ assert(subdir < NUM_SUBDIRS);
+
+ FOR_ALL_SEARCHPATHS(sp) {
+ FioGetFullPath(buf, buflen, sp, subdir, filename);
+ if (FileExists(buf)) break;
}
- f = fopen(buf, "rb");
+ return buf;
+}
+
+char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
+{
+ assert(subdir < NUM_SUBDIRS);
+ assert(sp < NUM_SEARCHPATHS);
+
+ snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
+ return buf;
+}
+
+char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
+{
+ Searchpath sp;
+
+ /* Find and return the first valid directory */
+ FOR_ALL_SEARCHPATHS(sp) {
+ char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
+ if (FileExists(buf)) return ret;
+ }
+
+ /* Could not find the directory, fall back to a base path */
+ ttd_strlcpy(buf, _personal_dir, buflen);
+
+ return buf;
+}
+
+FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir)
+{
+#if defined(WIN32) && defined(UNICODE)
+ /* fopen is implemented as a define with ellipses for
+ * Unicode support (prepend an L). As we are not sending
+ * a string, but a variable, it 'renames' the variable,
+ * so make that variable to makes it compile happily */
+ wchar_t Lmode[5];
+ MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
+#endif
+ FILE *f = NULL;
+ char buf[MAX_PATH];
+
+ if (subdir == NO_DIRECTORY) {
+ ttd_strlcpy(buf, filename, lengthof(buf));
+ } else {
+ snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
+ }
+
+ f = fopen(buf, mode);
#if !defined(WIN32)
if (f == NULL) {
- strtolower(strrchr(buf, PATHSEPCHAR));
- f = fopen(buf, "rb");
-
-#if defined SECOND_DATA_DIR
- /* tries in the 2nd data directory */
- if (f == NULL) {
- snprintf(buf, lengthof(buf), "%s%s", _paths.second_data_dir, filename);
- strtolower(buf + strlen(_paths.second_data_dir) - 1);
- f = fopen(buf, "rb");
- }
-#endif
+ strtolower(buf + strlen(_searchpaths[sp]) - 1);
+ f = fopen(buf, mode);
}
#endif
+ return f;
+}
+
+/** Opens OpenTTD files somewhere in a personal or global directory */
+FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir)
+{
+ FILE *f = NULL;
+ Searchpath sp;
+
+ assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
+
+ FOR_ALL_SEARCHPATHS(sp) {
+ f = FioFOpenFileSp(filename, mode, sp, subdir);
+ if (f != NULL || subdir == NO_DIRECTORY) break;
+ }
return f;
}
@@ -292,7 +371,8 @@
#if defined(WIN32) || defined(WINCE)
/**
* Determine the base (personal dir and game data dir) paths
- * @param exe the path to the executable
+ * @param exe the path from the current path to the executable
+ * @note defined in the OS related files (os2.cpp, win32.cpp, unix.cpp etc)
*/
extern void DetermineBasePaths(const char *exe);
#else /* defined(WIN32) || defined(WINCE) */
@@ -329,68 +409,123 @@
*/
void DetermineBasePaths(const char *exe)
{
- /* Change the working directory to enable doubleclicking in UIs */
- ChangeWorkingDirectory(exe);
-
- _paths.game_data_dir = BuildWithFullPath(GAME_DATA_DIR);
-#if defined(SECOND_DATA_DIR)
- _paths.second_data_dir = BuildWithFullPath(SECOND_DATA_DIR);
-#else
- _paths.second_data_dir = NULL;
-#endif
-
-#if defined(USE_HOMEDIR)
+ char tmp[MAX_PATH];
+#ifdef WITH_PERSONAL_DIR
const char *homedir = getenv("HOME");
if (homedir == NULL) {
const struct passwd *pw = getpwuid(getuid());
- if (pw != NULL) homedir = pw->pw_dir;
+ homedir = (pw == NULL) ? "" : pw->pw_dir;
}
- _paths.personal_dir = str_fmt("%s" PATHSEP "%s", homedir, PERSONAL_DIR);
- AppendPathSeparator(_paths.personal_dir, MAX_PATH);
-#else /* not defined(USE_HOMEDIR) */
- _paths.personal_dir = BuildWithFullPath(PERSONAL_DIR);
-#endif /* defined(USE_HOMEDIR) */
+ snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
+ AppendPathSeparator(tmp, MAX_PATH);
+
+ _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
+#else
+ _searchpaths[SP_PERSONAL_DIR] = NULL;
+#endif
+ _searchpaths[SP_SHARED_DIR] = NULL;
+
+ getcwd(tmp, MAX_PATH);
+ AppendPathSeparator(tmp, MAX_PATH);
+ _searchpaths[SP_WORKING_DIR] = strdup(tmp);
+
+ /* Change the working directory to that one of the executable */
+ ChangeWorkingDirectory((char*)exe);
+ getcwd(tmp, MAX_PATH);
+ AppendPathSeparator(tmp, MAX_PATH);
+ _searchpaths[SP_BINARY_DIR] = strdup(tmp);
+
+ snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
+ AppendPathSeparator(tmp, MAX_PATH);
+ _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
+#ifdef WITH_COCOA
+extern void cocoaSetApplicationBundleDir();
+ cocoaSetApplicationBundleDir();
+#else
+ _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
+#endif
}
#endif /* defined(WIN32) || defined(WINCE) */
+char *_personal_dir;
+
/**
* Acquire the base paths (personal dir and game data dir),
* fill all other paths (save dir, autosave dir etc) and
* make the save and scenario directories.
- * @param exe the path to the executable
- * @todo for save_dir, autosave_dir, scenario_dir and heightmap_dir the
- * assumption is that there is no path separator, however for gm_dir
- * lang_dir and data_dir that assumption is made.
- * This inconsistency should be resolved.
+ * @param exe the path from the current path to the executable
*/
void DeterminePaths(const char *exe)
{
DetermineBasePaths(exe);
- _paths.save_dir = str_fmt("%ssave" PATHSEP, _paths.personal_dir);
- _paths.autosave_dir = str_fmt("%s" PATHSEP "autosave" PATHSEP, _paths.save_dir);
- _paths.scenario_dir = str_fmt("%sscenario" PATHSEP, _paths.personal_dir);
- _paths.heightmap_dir = str_fmt("%s" PATHSEP "heightmap" PATHSEP, _paths.scenario_dir);
- _paths.gm_dir = str_fmt("%sgm" PATHSEP, _paths.game_data_dir);
- _paths.data_dir = str_fmt("%sdata" PATHSEP, _paths.game_data_dir);
-#if defined(CUSTOM_LANG_DIR)
- _paths.lang_dir = BuildWithFullPath(CUSTOM_LANG_DIR);
-#else
- _paths.lang_dir = str_fmt("%slang" PATHSEP, _paths.game_data_dir);
-#endif
+ Searchpath sp;
+ FOR_ALL_SEARCHPATHS(sp) DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
- if (_config_file == NULL) {
- _config_file = str_fmt("%sopenttd.cfg", _paths.personal_dir);
+ if (_config_file != NULL) {
+ _personal_dir = strdup(_config_file);
+ char *end = strrchr(_personal_dir , PATHSEPCHAR);
+ if (end == NULL) {
+ _personal_dir[0] = '\0';
+ } else {
+ end[1] = '\0';
+ }
+ } else {
+ char personal_dir[MAX_PATH];
+ FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg");
+
+ if (FileExists(personal_dir)) {
+ char *end = strrchr(personal_dir, PATHSEPCHAR);
+ if (end != NULL) end[1] = '\0';
+ _personal_dir = strdup(personal_dir);
+ _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
+ } else {
+ static const Searchpath new_openttd_cfg_order[] = {
+ SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
+ };
+
+ for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
+ if (IsValidSearchPath(new_openttd_cfg_order[i])) {
+ _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
+ _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
+ break;
+ }
+ }
+ }
}
- _highscore_file = str_fmt("%shs.dat", _paths.personal_dir);
- _log_file = str_fmt("%sopenttd.log", _paths.personal_dir);
+ DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
- /* Make (auto)save and scenario folder */
- FioCreateDirectory(_paths.save_dir);
- FioCreateDirectory(_paths.autosave_dir);
- FioCreateDirectory(_paths.scenario_dir);
- FioCreateDirectory(_paths.heightmap_dir);
+ _highscore_file = str_fmt("%shs.dat", _personal_dir);
+ _log_file = str_fmt("%sopenttd.log", _personal_dir);
+
+ char *save_dir = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(SAVE_DIR));
+ char *autosave_dir = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(AUTOSAVE_DIR));
+
+ /* Make the necessary folders */
+ FioCreateDirectory(_personal_dir);
+ FioCreateDirectory(save_dir);
+ FioCreateDirectory(autosave_dir);
+
+ free(save_dir);
+ free(autosave_dir);
}
+
+/**
+ * Sanitizes a filename, i.e. removes all illegal characters from it.
+ * @param filename the "\0" terminated filename
+ */
+void SanitizeFilename(char *filename)
+{
+ for (; *filename != '\0'; filename++) {
+ switch (*filename) {
+ /* The following characters are not allowed in filenames
+ * on at least one of the supported operating systems: */
+ case ':': case '\\': case '*': case '?': case '/':
+ *filename = '_';
+ break;
+ }
+ }
+}