src/fileio.cpp
changeset 6929 56470c1b8a66
parent 6896 b96972ff7d4d
child 6935 0a2a174d3f6a
equal deleted inserted replaced
6928:f1d8bf0d6f45 6929:56470c1b8a66
     9 #include "string.h"
     9 #include "string.h"
    10 #include "macros.h"
    10 #include "macros.h"
    11 #include "variables.h"
    11 #include "variables.h"
    12 #include "debug.h"
    12 #include "debug.h"
    13 #include "fios.h"
    13 #include "fios.h"
    14 #ifndef WIN32
    14 #ifdef WIN32
       
    15 #include <windows.h>
       
    16 #else
    15 #include <pwd.h>
    17 #include <pwd.h>
    16 #include <unistd.h>
    18 #include <unistd.h>
    17 #include <sys/stat.h>
    19 #include <sys/stat.h>
    18 #endif
    20 #endif
    19 
    21 
   187 	_fio.open_handles++;
   189 	_fio.open_handles++;
   188 #endif /* LIMITED_FDS */
   190 #endif /* LIMITED_FDS */
   189 	FioSeekToFile(slot << 24);
   191 	FioSeekToFile(slot << 24);
   190 }
   192 }
   191 
   193 
       
   194 const char *_subdirs[NUM_SUBDIRS] = {
       
   195 	"",
       
   196 	"save" PATHSEP,
       
   197 	"save" PATHSEP "autosave" PATHSEP,
       
   198 	"scenario" PATHSEP,
       
   199 	"scenario" PATHSEP "heightmap" PATHSEP,
       
   200 	"gm" PATHSEP,
       
   201 	"data" PATHSEP,
       
   202 	"lang" PATHSEP
       
   203 };
       
   204 
       
   205 const char *_searchpaths[NUM_SEARCHPATHS];
       
   206 
   192 /**
   207 /**
   193  * Check whether the given file exists
   208  * Check whether the given file exists
   194  * @param filename the file to try for existance
   209  * @param filename the file to try for existance
       
   210  * @param subdir the subdirectory to look in
   195  * @return true if and only if the file can be opened
   211  * @return true if and only if the file can be opened
   196  */
   212  */
   197 bool FioCheckFileExists(const char *filename)
   213 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
   198 {
   214 {
   199 	FILE *f = FioFOpenFile(filename);
   215 	FILE *f = FioFOpenFile(filename, "rb", subdir);
   200 	if (f == NULL) return false;
   216 	if (f == NULL) return false;
   201 
   217 
   202 	fclose(f);
   218 	fclose(f);
   203 	return true;
   219 	return true;
   204 }
   220 }
   205 
   221 
   206 /**
   222 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
   207  * Opens the file with the given name
   223 {
   208  * @param filename the file to open (in either data_dir or second_data_dir)
   224 	assert(subdir < NUM_SUBDIRS);
   209  * @return the opened file or NULL when it failed.
   225 	assert(sp < NUM_SEARCHPATHS);
   210  */
   226 
   211 FILE *FioFOpenFile(const char *filename)
   227 	snprintf(buf, buflen, "%s%s" PATHSEP "%s", _searchpaths[sp], _subdirs[subdir], filename);
   212 {
   228 	return buf;
   213 	FILE *f;
   229 }
       
   230 
       
   231 char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
       
   232 {
       
   233 	Searchpath sp;
       
   234 	assert(subdir < NUM_SUBDIRS);
       
   235 
       
   236 	FOR_ALL_SEARCHPATHS(sp) {
       
   237 		FioGetFullPath(buf, buflen, sp, subdir, filename);
       
   238 		if (FileExists(buf)) break;
       
   239 	}
       
   240 
       
   241 	return buf;
       
   242 }
       
   243 
       
   244 char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
       
   245 {
       
   246 	assert(subdir < NUM_SUBDIRS);
       
   247 	assert(sp < NUM_SEARCHPATHS);
       
   248 
       
   249 	snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
       
   250 	return buf;
       
   251 }
       
   252 
       
   253 char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
       
   254 {
       
   255 	Searchpath sp;
       
   256 
       
   257 	/* Find and return the first valid directory */
       
   258 	FOR_ALL_SEARCHPATHS(sp) {
       
   259 		char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
       
   260 		if (FileExists(buf)) return ret;
       
   261 	}
       
   262 
       
   263 	/* Could not find the directory, fall back to a base path */
       
   264 	ttd_strlcpy(buf, _personal_dir, buflen);
       
   265 
       
   266 	return buf;
       
   267 }
       
   268 
       
   269 FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir)
       
   270 {
       
   271 #if defined(WIN32) && defined(UNICODE)
       
   272 	/* fopen is implemented as a define with ellipses for
       
   273 	 * Unicode support (prepend an L). As we are not sending
       
   274 	 * a string, but a variable, it 'renames' the variable,
       
   275 	 * so make that variable to makes it compile happily */
       
   276 	wchar_t Lmode[5];
       
   277 	MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
       
   278 #endif
       
   279 	FILE *f = NULL;
   214 	char buf[MAX_PATH];
   280 	char buf[MAX_PATH];
   215 
   281 
   216 	if (filename[0] == PATHSEPCHAR || filename[1] == ':') {
   282 	if (subdir == BASE_DIR) {
   217 		ttd_strlcpy(buf, filename, lengthof(buf));
   283 		ttd_strlcpy(buf, filename, lengthof(buf));
   218 	} else {
   284 	} else {
   219 		snprintf(buf, lengthof(buf), "%s%s", _paths.data_dir, filename);
   285 		snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
   220 	}
   286 	}
   221 
   287 
   222 	f = fopen(buf, "rb");
   288 	f = fopen(buf, mode);
   223 #if !defined(WIN32)
   289 #if !defined(WIN32)
   224 	if (f == NULL) {
   290 	if (f == NULL) {
   225 		strtolower(strrchr(buf, PATHSEPCHAR));
   291 		strtolower(buf + strlen(_searchpaths[sp]) - 1);
   226 		f = fopen(buf, "rb");
   292 		f = fopen(buf, mode);
   227 
   293 	}
   228 #if defined SECOND_DATA_DIR
       
   229 		/* tries in the 2nd data directory */
       
   230 		if (f == NULL) {
       
   231 			snprintf(buf, lengthof(buf), "%s%s", _paths.second_data_dir, filename);
       
   232 			strtolower(buf + strlen(_paths.second_data_dir) - 1);
       
   233 			f = fopen(buf, "rb");
       
   234 		}
       
   235 #endif
   294 #endif
   236 	}
   295 	return f;
   237 #endif
   296 }
       
   297 
       
   298 /** Opens OpenTTD files somewhere in a personal or global directory */
       
   299 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir)
       
   300 {
       
   301 	FILE *f = NULL;
       
   302 	Searchpath sp;
       
   303 
       
   304 	assert(subdir < NUM_SUBDIRS);
       
   305 
       
   306 	FOR_ALL_SEARCHPATHS(sp) {
       
   307 		f = FioFOpenFileSp(filename, mode, sp, subdir);
       
   308 		if (f != NULL || subdir == 0) break;
       
   309 	}
   238 
   310 
   239 	return f;
   311 	return f;
   240 }
   312 }
   241 
   313 
   242 /**
   314 /**
   297 }
   369 }
   298 
   370 
   299 #if defined(WIN32) || defined(WINCE)
   371 #if defined(WIN32) || defined(WINCE)
   300 /**
   372 /**
   301  * Determine the base (personal dir and game data dir) paths
   373  * Determine the base (personal dir and game data dir) paths
   302  * @param exe the path to the executable
   374  * @param exe the path from the current path to the executable
       
   375  * @note defined in the OS related files (os2.cpp, win32.cpp, unix.cpp etc)
   303  */
   376  */
   304 extern void DetermineBasePaths(const char *exe);
   377 extern void DetermineBasePaths(const char *exe);
   305 #else /* defined(WIN32) || defined(WINCE) */
   378 #else /* defined(WIN32) || defined(WINCE) */
   306 
   379 
   307 /**
   380 /**
   334  * Determine the base (personal dir and game data dir) paths
   407  * Determine the base (personal dir and game data dir) paths
   335  * @param exe the path to the executable
   408  * @param exe the path to the executable
   336  */
   409  */
   337 void DetermineBasePaths(const char *exe)
   410 void DetermineBasePaths(const char *exe)
   338 {
   411 {
   339 	/* Change the working directory to enable doubleclicking in UIs */
   412 	char tmp[MAX_PATH];
   340 	ChangeWorkingDirectory(exe);
   413 #ifdef WITH_PERSONAL_DIR
   341 
       
   342 	_paths.game_data_dir = BuildWithFullPath(GAME_DATA_DIR);
       
   343 #if defined(SECOND_DATA_DIR)
       
   344 	_paths.second_data_dir = BuildWithFullPath(SECOND_DATA_DIR);
       
   345 #else
       
   346 	_paths.second_data_dir = NULL;
       
   347 #endif
       
   348 
       
   349 #if defined(USE_HOMEDIR)
       
   350 	const char *homedir = getenv("HOME");
   414 	const char *homedir = getenv("HOME");
   351 
   415 
   352 	if (homedir == NULL) {
   416 	if (homedir == NULL) {
   353 		const struct passwd *pw = getpwuid(getuid());
   417 		const struct passwd *pw = getpwuid(getuid());
   354 		if (pw != NULL) homedir = pw->pw_dir;
   418 		homedir = (pw == NULL) ? "" : pw->pw_dir;
   355 	}
   419 	}
   356 
   420 
   357 	_paths.personal_dir = str_fmt("%s" PATHSEP "%s", homedir, PERSONAL_DIR);
   421 	snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
   358 	AppendPathSeparator(_paths.personal_dir, MAX_PATH);
   422 	AppendPathSeparator(tmp, MAX_PATH);
   359 #else /* not defined(USE_HOMEDIR) */
   423 
   360 	_paths.personal_dir = BuildWithFullPath(PERSONAL_DIR);
   424 	_searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
   361 #endif /* defined(USE_HOMEDIR) */
   425 #else
       
   426 	_searchpaths[SP_PERSONAL_DIR] = NULL;
       
   427 #endif
       
   428 	_searchpaths[SP_SHARED_DIR] = NULL;
       
   429 
       
   430 	getcwd(tmp, MAX_PATH);
       
   431 	AppendPathSeparator(tmp, MAX_PATH);
       
   432 	_searchpaths[SP_WORKING_DIR] = strdup(tmp);
       
   433 
       
   434 	/* Change the working directory to that one of the executable */
       
   435 	ChangeWorkingDirectory((char*)exe);
       
   436 	getcwd(tmp, MAX_PATH);
       
   437 	AppendPathSeparator(tmp, MAX_PATH);
       
   438 	_searchpaths[SP_BINARY_DIR] = strdup(tmp);
       
   439 
       
   440 	snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
       
   441 	AppendPathSeparator(tmp, MAX_PATH);
       
   442 	_searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
       
   443 #ifdef WITH_COCOA
       
   444 extern void cocoaSetApplicationBundleDir();
       
   445 	cocoaSetApplicationBundleDir();
       
   446 #else
       
   447 	_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
       
   448 #endif
   362 }
   449 }
   363 #endif /* defined(WIN32) || defined(WINCE) */
   450 #endif /* defined(WIN32) || defined(WINCE) */
       
   451 
       
   452 char *_personal_dir;
   364 
   453 
   365 /**
   454 /**
   366  * Acquire the base paths (personal dir and game data dir),
   455  * Acquire the base paths (personal dir and game data dir),
   367  * fill all other paths (save dir, autosave dir etc) and
   456  * fill all other paths (save dir, autosave dir etc) and
   368  * make the save and scenario directories.
   457  * make the save and scenario directories.
   369  * @param exe the path to the executable
   458  * @param exe the path from the current path to the executable
   370  * @todo for save_dir, autosave_dir, scenario_dir and heightmap_dir the
       
   371  *       assumption is that there is no path separator, however for gm_dir
       
   372  *       lang_dir and data_dir that assumption is made.
       
   373  *       This inconsistency should be resolved.
       
   374  */
   459  */
   375 void DeterminePaths(const char *exe)
   460 void DeterminePaths(const char *exe)
   376 {
   461 {
   377 	DetermineBasePaths(exe);
   462 	DetermineBasePaths(exe);
   378 
   463 
   379 	_paths.save_dir      = str_fmt("%ssave" PATHSEP, _paths.personal_dir);
   464 	Searchpath sp;
   380 	_paths.autosave_dir  = str_fmt("%s" PATHSEP "autosave" PATHSEP, _paths.save_dir);
   465 	FOR_ALL_SEARCHPATHS(sp) DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
   381 	_paths.scenario_dir  = str_fmt("%sscenario" PATHSEP, _paths.personal_dir);
   466 
   382 	_paths.heightmap_dir = str_fmt("%s" PATHSEP "heightmap" PATHSEP, _paths.scenario_dir);
   467 	/* Search for the first search path, as that will be the closest to
   383 	_paths.gm_dir        = str_fmt("%sgm" PATHSEP, _paths.game_data_dir);
   468 	 * the personal directory. */
   384 	_paths.data_dir      = str_fmt("%sdata" PATHSEP, _paths.game_data_dir);
   469 	FOR_ALL_SEARCHPATHS(sp) {
   385 #if defined(CUSTOM_LANG_DIR)
   470 		_personal_dir = strdup(_searchpaths[sp]);
   386 	_paths.lang_dir = BuildWithFullPath(CUSTOM_LANG_DIR);
   471 		DEBUG(misc, 3, "%s found as personal directory", _searchpaths[sp]);
   387 #else
   472 		break;
   388 	_paths.lang_dir = str_fmt("%slang" PATHSEP, _paths.game_data_dir);
   473 	}
   389 #endif
       
   390 
   474 
   391 	if (_config_file == NULL) {
   475 	if (_config_file == NULL) {
   392 		_config_file = str_fmt("%sopenttd.cfg", _paths.personal_dir);
   476 		_config_file = str_fmt("%sopenttd.cfg", _personal_dir);
   393 	}
   477 	}
   394 
   478 
   395 	_highscore_file = str_fmt("%shs.dat", _paths.personal_dir);
   479 	_highscore_file = str_fmt("%shs.dat", _personal_dir);
   396 	_log_file = str_fmt("%sopenttd.log",  _paths.personal_dir);
   480 	_log_file = str_fmt("%sopenttd.log",  _personal_dir);
   397 
   481 
   398 	/* Make (auto)save and scenario folder */
   482 	char *save_dir     = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(SAVE_DIR));
   399 	FioCreateDirectory(_paths.save_dir);
   483 	char *autosave_dir = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(AUTOSAVE_DIR));
   400 	FioCreateDirectory(_paths.autosave_dir);
   484 
   401 	FioCreateDirectory(_paths.scenario_dir);
   485 	/* Make the necessary folders */
   402 	FioCreateDirectory(_paths.heightmap_dir);
   486 	FioCreateDirectory(_personal_dir);
       
   487 	FioCreateDirectory(save_dir);
       
   488 	FioCreateDirectory(autosave_dir);
       
   489 
       
   490 	free(save_dir);
       
   491 	free(autosave_dir);
   403 }
   492 }
   404 
   493 
   405 /**
   494 /**
   406  * Sanitizes a filename, i.e. removes all illegal characters from it.
   495  * Sanitizes a filename, i.e. removes all illegal characters from it.
   407  * @param filename the "\0" terminated filename
   496  * @param filename the "\0" terminated filename