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 /** |
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 |