tron@2186: /* $Id$ */ tron@2186: belugas@6201: /** @file fileio.cpp Standard In/Out file operations */ belugas@6179: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@1093: #include "fileio.h" tron@2163: #include "functions.h" Darkvater@4200: #include "string.h" tron@2159: #include "macros.h" tron@2153: #include "variables.h" truelight@5967: #include "debug.h" rubidium@6298: #include "fios.h" rubidium@6929: #ifdef WIN32 rubidium@6929: #include rubidium@6929: #else rubidium@6323: #include rubidium@6317: #include rubidium@6298: #include rubidium@6298: #endif truelight@0: truelight@0: /*************************************************/ truelight@0: /* FILE IO ROUTINES ******************************/ truelight@0: /*************************************************/ truelight@0: truelight@0: #define FIO_BUFFER_SIZE 512 truelight@5967: #define MAX_HANDLES 64 truelight@0: rubidium@6248: struct Fio { Darkvater@4203: byte *buffer, *buffer_end; ///< position pointer in local buffer and last valid byte of buffer Darkvater@4203: uint32 pos; ///< current (system) position in file Darkvater@4203: FILE *cur_fh; ///< current file handle truelight@6896: const char *filename; ///< current filename truelight@5967: FILE *handles[MAX_HANDLES]; ///< array of file handles we can have open Darkvater@4203: byte buffer_start[FIO_BUFFER_SIZE]; ///< local buffer when read from file truelight@6896: const char *filenames[MAX_HANDLES]; ///< array of filenames we (should) have open truelight@5967: #if defined(LIMITED_FDS) truelight@5967: uint open_handles; ///< current amount of open handles truelight@5967: uint usage_count[MAX_HANDLES]; ///< count how many times this file has been opened truelight@5967: #endif /* LIMITED_FDS */ rubidium@6248: }; truelight@0: truelight@0: static Fio _fio; truelight@0: belugas@6179: /* Get current position in file */ rubidium@6247: uint32 FioGetPos() truelight@0: { truelight@0: return _fio.pos + (_fio.buffer - _fio.buffer_start) - FIO_BUFFER_SIZE; truelight@0: } truelight@0: truelight@6896: const char *FioGetFilename() truelight@6896: { truelight@6896: return _fio.filename; truelight@6896: } truelight@6896: truelight@0: void FioSeekTo(uint32 pos, int mode) truelight@0: { truelight@0: if (mode == SEEK_CUR) pos += FioGetPos(); truelight@0: _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE; Darkvater@4203: _fio.pos = pos; Darkvater@4203: fseek(_fio.cur_fh, _fio.pos, SEEK_SET); truelight@0: } truelight@0: truelight@5967: #if defined(LIMITED_FDS) truelight@5967: static void FioRestoreFile(int slot) truelight@5967: { truelight@5967: /* Do we still have the file open, or should we reopen it? */ truelight@5967: if (_fio.handles[slot] == NULL) { truelight@7352: DEBUG(misc, 6, "Restoring file '%s' in slot '%d' from disk", _fio.filenames[slot], slot); truelight@7352: FioOpenFile(slot, _fio.filenames[slot]); truelight@5967: } truelight@5967: _fio.usage_count[slot]++; truelight@5967: } truelight@5967: #endif /* LIMITED_FDS */ truelight@5967: belugas@6179: /* Seek to a file and a position */ truelight@7570: void FioSeekToFile(uint8 slot, uint32 pos) truelight@0: { truelight@5967: FILE *f; truelight@5967: #if defined(LIMITED_FDS) truelight@5967: /* Make sure we have this file open */ truelight@7570: FioRestoreFile(slot); truelight@5967: #endif /* LIMITED_FDS */ truelight@7570: f = _fio.handles[slot]; truelight@0: assert(f != NULL); truelight@0: _fio.cur_fh = f; truelight@7570: _fio.filename = _fio.filenames[slot]; truelight@7570: FioSeekTo(pos, SEEK_SET); truelight@0: } truelight@0: rubidium@6247: byte FioReadByte() truelight@0: { truelight@0: if (_fio.buffer == _fio.buffer_end) { truelight@0: _fio.pos += FIO_BUFFER_SIZE; truelight@0: fread(_fio.buffer = _fio.buffer_start, 1, FIO_BUFFER_SIZE, _fio.cur_fh); truelight@0: } truelight@0: return *_fio.buffer++; truelight@0: } truelight@0: truelight@0: void FioSkipBytes(int n) truelight@0: { tron@2952: for (;;) { truelight@0: int m = min(_fio.buffer_end - _fio.buffer, n); truelight@0: _fio.buffer += m; truelight@0: n -= m; truelight@0: if (n == 0) break; truelight@0: FioReadByte(); truelight@0: n--; truelight@0: } truelight@0: } truelight@0: rubidium@6247: uint16 FioReadWord() truelight@0: { truelight@0: byte b = FioReadByte(); truelight@0: return (FioReadByte() << 8) | b; truelight@0: } truelight@0: rubidium@6247: uint32 FioReadDword() truelight@0: { truelight@0: uint b = FioReadWord(); truelight@0: return (FioReadWord() << 16) | b; truelight@0: } truelight@0: truelight@0: void FioReadBlock(void *ptr, uint size) truelight@0: { truelight@0: FioSeekTo(FioGetPos(), SEEK_SET); truelight@0: _fio.pos += size; truelight@0: fread(ptr, 1, size, _fio.cur_fh); truelight@0: } truelight@0: darkvater@1039: static inline void FioCloseFile(int slot) darkvater@1039: { darkvater@1039: if (_fio.handles[slot] != NULL) { tron@1109: fclose(_fio.handles[slot]); darkvater@1039: _fio.handles[slot] = NULL; truelight@5967: #if defined(LIMITED_FDS) truelight@5967: _fio.open_handles--; truelight@5967: #endif /* LIMITED_FDS */ darkvater@1039: } darkvater@1039: } darkvater@1039: rubidium@6247: void FioCloseAll() truelight@0: { truelight@0: int i; truelight@0: darkvater@1039: for (i = 0; i != lengthof(_fio.handles); i++) darkvater@1039: FioCloseFile(i); truelight@0: } truelight@0: truelight@5967: #if defined(LIMITED_FDS) truelight@5967: static void FioFreeHandle() truelight@5967: { truelight@5967: /* If we are about to open a file that will exceed the limit, close a file */ truelight@5967: if (_fio.open_handles + 1 == LIMITED_FDS) { truelight@5967: uint i, count; truelight@5967: int slot; truelight@5967: truelight@5967: count = UINT_MAX; truelight@5967: slot = -1; truelight@5967: /* Find the file that is used the least */ truelight@5967: for (i = 0; i < lengthof(_fio.handles); i++) { truelight@5967: if (_fio.handles[i] != NULL && _fio.usage_count[i] < count) { truelight@5967: count = _fio.usage_count[i]; truelight@5967: slot = i; truelight@5967: } truelight@5967: } truelight@5967: assert(slot != -1); truelight@7352: DEBUG(misc, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.filenames[slot], slot); truelight@5967: FioCloseFile(slot); truelight@5967: } truelight@5967: } truelight@5967: #endif /* LIMITED_FDS */ truelight@5967: rubidium@6299: void FioOpenFile(int slot, const char *filename) rubidium@6299: { rubidium@6299: FILE *f; rubidium@6299: rubidium@6299: #if defined(LIMITED_FDS) rubidium@6299: FioFreeHandle(); rubidium@6299: #endif /* LIMITED_FDS */ rubidium@6299: f = FioFOpenFile(filename); rubidium@6424: if (f == NULL) error("Cannot open file '%s'", filename); truelight@7570: uint32 pos = ftell(f); rubidium@6299: rubidium@6299: FioCloseFile(slot); // if file was opened before, close it rubidium@6299: _fio.handles[slot] = f; truelight@6896: _fio.filenames[slot] = filename; rubidium@6299: #if defined(LIMITED_FDS) rubidium@6299: _fio.usage_count[slot] = 0; rubidium@6299: _fio.open_handles++; rubidium@6299: #endif /* LIMITED_FDS */ truelight@7570: FioSeekToFile(slot, pos); rubidium@6299: } rubidium@6299: rubidium@6929: const char *_subdirs[NUM_SUBDIRS] = { rubidium@6929: "", rubidium@6929: "save" PATHSEP, rubidium@6929: "save" PATHSEP "autosave" PATHSEP, rubidium@6929: "scenario" PATHSEP, rubidium@6929: "scenario" PATHSEP "heightmap" PATHSEP, rubidium@6929: "gm" PATHSEP, rubidium@6929: "data" PATHSEP, rubidium@6929: "lang" PATHSEP rubidium@6929: }; rubidium@6929: rubidium@6929: const char *_searchpaths[NUM_SEARCHPATHS]; truelight@7581: std::list _tar_list; rubidium@6929: rubidium@6299: /** rubidium@6299: * Check whether the given file exists rubidium@6299: * @param filename the file to try for existance rubidium@6929: * @param subdir the subdirectory to look in rubidium@6299: * @return true if and only if the file can be opened rubidium@6299: */ rubidium@6929: bool FioCheckFileExists(const char *filename, Subdirectory subdir) rubidium@6299: { rubidium@6929: FILE *f = FioFOpenFile(filename, "rb", subdir); rubidium@6299: if (f == NULL) return false; rubidium@6299: rubidium@6299: fclose(f); rubidium@6299: return true; rubidium@6299: } rubidium@6299: rubidium@6929: char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename) bjarni@2736: { rubidium@6929: assert(subdir < NUM_SUBDIRS); rubidium@6929: assert(sp < NUM_SEARCHPATHS); bjarni@2736: rubidium@6941: snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename); rubidium@6929: return buf; rubidium@6929: } rubidium@6929: rubidium@6929: char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename) rubidium@6929: { rubidium@6929: Searchpath sp; rubidium@6929: assert(subdir < NUM_SUBDIRS); rubidium@6929: rubidium@6929: FOR_ALL_SEARCHPATHS(sp) { rubidium@6929: FioGetFullPath(buf, buflen, sp, subdir, filename); rubidium@6929: if (FileExists(buf)) break; rubidium@6424: } bjarni@2736: rubidium@6929: return buf; rubidium@6929: } rubidium@6929: rubidium@6929: char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir) rubidium@6929: { rubidium@6929: assert(subdir < NUM_SUBDIRS); rubidium@6929: assert(sp < NUM_SEARCHPATHS); rubidium@6929: rubidium@6929: snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]); rubidium@6929: return buf; rubidium@6929: } rubidium@6929: rubidium@6929: char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir) rubidium@6929: { rubidium@6929: Searchpath sp; rubidium@6929: rubidium@6929: /* Find and return the first valid directory */ rubidium@6929: FOR_ALL_SEARCHPATHS(sp) { rubidium@6929: char *ret = FioAppendDirectory(buf, buflen, sp, subdir); rubidium@6929: if (FileExists(buf)) return ret; rubidium@6929: } rubidium@6929: rubidium@6929: /* Could not find the directory, fall back to a base path */ rubidium@6929: ttd_strlcpy(buf, _personal_dir, buflen); rubidium@6929: rubidium@6929: return buf; rubidium@6929: } rubidium@6929: truelight@7574: FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize) rubidium@6929: { rubidium@6929: #if defined(WIN32) && defined(UNICODE) rubidium@6929: /* fopen is implemented as a define with ellipses for rubidium@6929: * Unicode support (prepend an L). As we are not sending rubidium@6929: * a string, but a variable, it 'renames' the variable, rubidium@6929: * so make that variable to makes it compile happily */ rubidium@6929: wchar_t Lmode[5]; rubidium@6929: MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode)); rubidium@6929: #endif rubidium@6929: FILE *f = NULL; rubidium@6929: char buf[MAX_PATH]; rubidium@6929: rubidium@6935: if (subdir == NO_DIRECTORY) { rubidium@6929: ttd_strlcpy(buf, filename, lengthof(buf)); rubidium@6929: } else { rubidium@6929: snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename); rubidium@6929: } rubidium@6929: rubidium@6929: f = fopen(buf, mode); bjarni@2736: #if !defined(WIN32) bjarni@2736: if (f == NULL) { rubidium@6929: strtolower(buf + strlen(_searchpaths[sp]) - 1); rubidium@6929: f = fopen(buf, mode); bjarni@2736: } bjarni@2736: #endif truelight@7574: if (f != NULL && filesize != NULL) { truelight@7574: /* Find the size of the file */ truelight@7574: fseek(f, 0, SEEK_END); truelight@7574: *filesize = ftell(f); truelight@7574: fseek(f, 0, SEEK_SET); truelight@7574: } rubidium@6929: return f; rubidium@6929: } rubidium@6929: truelight@7581: FILE *FioTarFileList(const char *tar, const char *mode, size_t *filesize, FioTarFileListCallback *callback, void *userdata) truelight@7581: { truelight@7581: /* The TAR-header, repeated for every file */ truelight@7581: typedef struct TarHeader { truelight@7581: char name[100]; ///< Name of the file truelight@7581: char mode[8]; truelight@7581: char uid[8]; truelight@7581: char gid[8]; truelight@7581: char size[12]; ///< Size of the file, in ASCII truelight@7581: char mtime[12]; truelight@7581: char chksum[8]; truelight@7581: char typeflag; truelight@7581: char linkname[100]; truelight@7581: char magic[6]; truelight@7581: char version[2]; truelight@7581: char uname[32]; truelight@7581: char gname[32]; truelight@7581: char devmajor[8]; truelight@7581: char devminor[8]; truelight@7581: char prefix[155]; ///< Path of the file truelight@7581: truelight@7581: char unused[12]; truelight@7581: } TarHeader; truelight@7581: truelight@7581: assert(mode[0] == 'r'); // Only reading is supported truelight@7581: assert(callback != NULL); // We need a callback, else this function doens't do much truelight@7581: truelight@7581: #if defined(WIN32) && defined(UNICODE) truelight@7581: /* fopen is implemented as a define with ellipses for truelight@7581: * Unicode support (prepend an L). As we are not sending truelight@7581: * a string, but a variable, it 'renames' the variable, truelight@7581: * so make that variable to makes it compile happily */ truelight@7581: wchar_t Lmode[5]; truelight@7581: MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode)); truelight@7581: #endif truelight@7581: truelight@7581: FILE *f = fopen(tar, mode); truelight@7581: assert(f != NULL); truelight@7581: truelight@7581: TarHeader th; truelight@7581: char buf[sizeof(th.name) + 1], *end; truelight@7581: char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1]; truelight@7581: truelight@7581: while (!feof(f)) { truelight@7581: /* Read the header and make sure it is a valid one */ truelight@7581: fread(&th, 1, 512, f); truelight@7581: if (strncmp(th.magic, "ustar", 5) != 0) return NULL; truelight@7581: truelight@7581: name[0] = '\0'; truelight@7581: int len = 0; truelight@7581: truelight@7581: /* The prefix contains the directory-name */ truelight@7581: if (th.prefix[0] != '\0') { truelight@7581: memcpy(name, th.prefix, sizeof(th.prefix)); truelight@7581: name[sizeof(th.prefix)] = '\0'; truelight@7581: len = strlen(name); truelight@7581: name[len] = PATHSEPCHAR; truelight@7581: len++; truelight@7581: } truelight@7581: truelight@7581: /* Copy the name of the file in a safe way at the end of 'name' */ truelight@7581: memcpy(&name[len], th.name, sizeof(th.name)); truelight@7581: name[len + sizeof(th.name)] = '\0'; truelight@7581: truelight@7581: /* Calculate the size of the file.. for some strange reason this is stored as a string */ truelight@7581: memcpy(buf, th.size, sizeof(th.size)); truelight@7581: buf[sizeof(th.size)] = '\0'; truelight@7581: int skip = strtol(buf, &end, 8); truelight@7581: truelight@7581: /* Check in the callback if this is the file we want */ truelight@7581: if (callback(name, skip, userdata)) { truelight@7581: if (filesize != NULL) *filesize = skip; truelight@7581: return f; truelight@7581: } truelight@7581: truelight@7581: /* Skip to the next block.. */ truelight@7581: fseek(f, ALIGN(skip, 512), SEEK_CUR); truelight@7581: } truelight@7581: truelight@7581: fclose(f); truelight@7581: return NULL; truelight@7581: } truelight@7581: truelight@7581: bool FioFOpenFileTarFileListCallback(const char *filename, int size, void *search_filename) truelight@7581: { truelight@7581: return strcasecmp(filename, (const char *)search_filename) == 0; truelight@7581: } truelight@7581: truelight@7581: FILE *FioFOpenFileTar(const char *filename, const char *tar_filename, size_t *filesize) truelight@7581: { truelight@7581: return FioTarFileList(tar_filename, "rb", filesize, FioFOpenFileTarFileListCallback, (void *)filename); truelight@7581: } truelight@7581: rubidium@6929: /** Opens OpenTTD files somewhere in a personal or global directory */ truelight@7575: FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize) rubidium@6929: { rubidium@6929: FILE *f = NULL; rubidium@6929: Searchpath sp; rubidium@6929: rubidium@6935: assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY); rubidium@6929: rubidium@6929: FOR_ALL_SEARCHPATHS(sp) { truelight@7575: f = FioFOpenFileSp(filename, mode, sp, subdir, filesize); rubidium@6935: if (f != NULL || subdir == NO_DIRECTORY) break; rubidium@6929: } truelight@7581: /* We can only use .tar in case of data-dir, and read-mode */ truelight@7581: if (f == NULL && subdir == DATA_DIR && mode[0] == 'r') { truelight@7581: const char *tar; truelight@7581: FOR_ALL_TARS(tar) { truelight@7581: f = FioFOpenFileTar(filename, tar, filesize); truelight@7581: if (f != NULL) break; truelight@7581: } truelight@7581: } bjarni@2736: bjarni@2736: return f; bjarni@2736: } bjarni@2736: rubidium@6298: /** rubidium@6298: * Create a directory with the given name rubidium@6298: * @param name the new name of the directory rubidium@6298: */ rubidium@6298: void FioCreateDirectory(const char *name) rubidium@6298: { rubidium@6298: #if defined(WIN32) || defined(WINCE) rubidium@6298: CreateDirectory(OTTD2FS(name), NULL); rubidium@6298: #elif defined(OS2) && !defined(__INNOTEK_LIBC__) rubidium@6298: mkdir(OTTD2FS(name)); rubidium@6298: #else rubidium@6298: mkdir(OTTD2FS(name), 0755); rubidium@6298: #endif rubidium@6298: } rubidium@6298: rubidium@6298: /** rubidium@6298: * Appends, if necessary, the path separator character to the end of the string. rubidium@6298: * It does not add the path separator to zero-sized strings. rubidium@6298: * @param buf string to append the separator to rubidium@6298: * @param buflen the length of the buf rubidium@6298: */ rubidium@6298: void AppendPathSeparator(char *buf, size_t buflen) rubidium@6298: { rubidium@6298: size_t s = strlen(buf); rubidium@6298: rubidium@6298: /* Length of string + path separator + '\0' */ rubidium@6298: if (s != 0 && buf[s - 1] != PATHSEPCHAR && s + 2 < buflen) { rubidium@6299: buf[s] = PATHSEPCHAR; rubidium@6299: buf[s + 1] = '\0'; rubidium@6298: } rubidium@6298: } rubidium@6298: rubidium@6834: /** rubidium@6834: * Allocates and files a variable with the full path rubidium@6834: * based on the given directory. rubidium@6834: * @param dir the directory to base the path on rubidium@6834: * @return the malloced full path rubidium@6834: */ rubidium@6834: char *BuildWithFullPath(const char *dir) rubidium@6834: { rubidium@6834: char *dest = MallocT(MAX_PATH); rubidium@6834: ttd_strlcpy(dest, dir, MAX_PATH); rubidium@6834: rubidium@6834: /* Check if absolute or relative path */ rubidium@6834: const char *s = strchr(dest, PATHSEPCHAR); rubidium@6834: rubidium@6834: /* Add absolute path */ rubidium@6834: if (s == NULL || dest != s) { rubidium@6834: getcwd(dest, MAX_PATH); rubidium@6834: AppendPathSeparator(dest, MAX_PATH); rubidium@6834: ttd_strlcat(dest, dir, MAX_PATH); rubidium@6834: } rubidium@6834: AppendPathSeparator(dest, MAX_PATH); rubidium@6834: rubidium@6834: return dest; rubidium@6834: } rubidium@6834: rubidium@6317: #if defined(WIN32) || defined(WINCE) rubidium@6298: /** rubidium@6298: * Determine the base (personal dir and game data dir) paths rubidium@6929: * @param exe the path from the current path to the executable rubidium@6929: * @note defined in the OS related files (os2.cpp, win32.cpp, unix.cpp etc) rubidium@6298: */ rubidium@6317: extern void DetermineBasePaths(const char *exe); rubidium@6317: #else /* defined(WIN32) || defined(WINCE) */ rubidium@6317: rubidium@6317: /** rubidium@6317: * Changes the working directory to the path of the give executable. rubidium@6317: * For OSX application bundles '.app' is the required extension of the bundle, rubidium@6317: * so when we crop the path to there, when can remove the name of the bundle rubidium@6317: * in the same way we remove the name from the executable name. rubidium@6317: * @param exe the path to the executable rubidium@6317: */ rubidium@6317: void ChangeWorkingDirectory(const char *exe) rubidium@6317: { rubidium@6317: #ifdef WITH_COCOA rubidium@6317: char *app_bundle = strchr(exe, '.'); rubidium@6317: while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.'); rubidium@6317: rubidium@6317: if (app_bundle != NULL) app_bundle[0] = '\0'; rubidium@6317: #endif /* WITH_COCOA */ rubidium@6317: char *s = strrchr(exe, PATHSEPCHAR); rubidium@6317: if (s != NULL) { rubidium@6317: *s = '\0'; rubidium@6317: chdir(exe); rubidium@6317: *s = PATHSEPCHAR; rubidium@6317: } rubidium@6317: #ifdef WITH_COCOA rubidium@6317: if (app_bundle != NULL) app_bundle[0] = '.'; rubidium@6317: #endif /* WITH_COCOA */ rubidium@6317: } rubidium@6317: truelight@7581: static bool TarListAddFile(const char *filename) truelight@7581: { truelight@7581: /* See if we already have a tar by that name; useless to have double entries in our list */ truelight@7581: const char *tar; truelight@7581: FOR_ALL_TARS(tar) { truelight@7581: if (strcmp(tar, filename) == 0) return false; truelight@7581: } truelight@7581: truelight@7581: DEBUG(misc, 1, "Found tar: %s", filename); truelight@7581: _tar_list.push_back(strdup(filename)); truelight@7581: truelight@7581: return true; truelight@7581: } truelight@7581: truelight@7581: static int ScanPathForTarFiles(const char *path, int basepath_length) truelight@7581: { truelight@7581: extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb); truelight@7581: truelight@7581: uint num = 0; truelight@7581: struct stat sb; truelight@7581: struct dirent *dirent; truelight@7581: DIR *dir; truelight@7581: truelight@7581: if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0; truelight@7581: truelight@7581: while ((dirent = readdir(dir)) != NULL) { truelight@7581: const char *d_name = FS2OTTD(dirent->d_name); truelight@7581: char filename[MAX_PATH]; truelight@7581: truelight@7581: if (!FiosIsValidFile(path, dirent, &sb)) continue; truelight@7581: truelight@7581: snprintf(filename, lengthof(filename), "%s%s", path, d_name); truelight@7581: truelight@7581: if (sb.st_mode & S_IFDIR) { truelight@7581: /* Directory */ truelight@7581: if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue; truelight@7581: AppendPathSeparator(filename, lengthof(filename)); truelight@7581: num += ScanPathForTarFiles(filename, basepath_length); truelight@7581: } else if (sb.st_mode & S_IFREG) { truelight@7581: /* File */ truelight@7581: char *ext = strrchr(filename, '.'); truelight@7581: truelight@7581: /* If no extension or extension isn't .tar, skip the file */ truelight@7581: if (ext == NULL) continue; truelight@7581: if (strcasecmp(ext, ".tar") != 0) continue; truelight@7581: truelight@7581: if (TarListAddFile(filename)) num++; truelight@7581: } truelight@7581: } truelight@7581: truelight@7581: closedir(dir); truelight@7581: return num; truelight@7581: } truelight@7581: truelight@7581: static void ScanForTarFiles() truelight@7581: { truelight@7581: Searchpath sp; truelight@7581: char path[MAX_PATH]; truelight@7581: uint num = 0; truelight@7581: truelight@7581: DEBUG(misc, 1, "Scanning for tars"); truelight@7581: FOR_ALL_SEARCHPATHS(sp) { truelight@7581: FioAppendDirectory(path, MAX_PATH, sp, DATA_DIR); truelight@7581: num += ScanPathForTarFiles(path, strlen(path)); truelight@7581: } truelight@7581: DEBUG(misc, 1, "Scan complete, found %d files", num); truelight@7581: } truelight@7581: rubidium@6317: /** rubidium@6317: * Determine the base (personal dir and game data dir) paths rubidium@6317: * @param exe the path to the executable rubidium@6317: */ rubidium@6317: void DetermineBasePaths(const char *exe) rubidium@6317: { rubidium@6929: char tmp[MAX_PATH]; rubidium@6990: #if defined(__MORPHOS__) || defined(__AMIGA__) || !defined(WITH_PERSONAL_DIR) rubidium@6990: _searchpaths[SP_PERSONAL_DIR] = NULL; rubidium@6990: #else rubidium@6317: const char *homedir = getenv("HOME"); rubidium@6317: rubidium@6317: if (homedir == NULL) { rubidium@6317: const struct passwd *pw = getpwuid(getuid()); rubidium@6929: homedir = (pw == NULL) ? "" : pw->pw_dir; rubidium@6317: } rubidium@6317: rubidium@6929: snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR); rubidium@6929: AppendPathSeparator(tmp, MAX_PATH); rubidium@6929: rubidium@6929: _searchpaths[SP_PERSONAL_DIR] = strdup(tmp); rubidium@6929: #endif rubidium@6929: _searchpaths[SP_SHARED_DIR] = NULL; rubidium@6929: rubidium@6990: #if defined(__MORPHOS__) || defined(__AMIGA__) rubidium@6990: _searchpaths[SP_WORKING_DIR] = NULL; rubidium@6990: #else rubidium@6929: getcwd(tmp, MAX_PATH); rubidium@6929: AppendPathSeparator(tmp, MAX_PATH); rubidium@6929: _searchpaths[SP_WORKING_DIR] = strdup(tmp); rubidium@6990: #endif rubidium@6929: rubidium@6929: /* Change the working directory to that one of the executable */ rubidium@6929: ChangeWorkingDirectory((char*)exe); rubidium@6929: getcwd(tmp, MAX_PATH); rubidium@6929: AppendPathSeparator(tmp, MAX_PATH); rubidium@6929: _searchpaths[SP_BINARY_DIR] = strdup(tmp); rubidium@6929: rubidium@6990: #if defined(__MORPHOS__) || defined(__AMIGA__) rubidium@6990: _searchpaths[SP_INSTALLATION_DIR] = NULL; rubidium@6990: #else rubidium@6929: snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR); rubidium@6929: AppendPathSeparator(tmp, MAX_PATH); rubidium@6929: _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp); rubidium@6990: #endif rubidium@6929: #ifdef WITH_COCOA rubidium@6929: extern void cocoaSetApplicationBundleDir(); rubidium@6929: cocoaSetApplicationBundleDir(); rubidium@6929: #else rubidium@6929: _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL; rubidium@6929: #endif truelight@7581: truelight@7581: ScanForTarFiles(); rubidium@6317: } rubidium@6317: #endif /* defined(WIN32) || defined(WINCE) */ rubidium@6298: rubidium@6929: char *_personal_dir; rubidium@6929: rubidium@6298: /** rubidium@6298: * Acquire the base paths (personal dir and game data dir), rubidium@6298: * fill all other paths (save dir, autosave dir etc) and rubidium@6298: * make the save and scenario directories. rubidium@6929: * @param exe the path from the current path to the executable rubidium@6298: */ rubidium@6317: void DeterminePaths(const char *exe) rubidium@6298: { rubidium@6317: DetermineBasePaths(exe); rubidium@6298: rubidium@6929: Searchpath sp; rubidium@6929: FOR_ALL_SEARCHPATHS(sp) DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]); rubidium@6929: rubidium@6941: if (_config_file != NULL) { rubidium@6941: _personal_dir = strdup(_config_file); rubidium@6941: char *end = strrchr(_personal_dir , PATHSEPCHAR); rubidium@6941: if (end == NULL) { rubidium@6941: _personal_dir[0] = '\0'; rubidium@6941: } else { rubidium@6941: end[1] = '\0'; rubidium@6941: } rubidium@6941: } else { rubidium@6941: char personal_dir[MAX_PATH]; rubidium@6941: FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg"); rubidium@6941: rubidium@6941: if (FileExists(personal_dir)) { rubidium@6941: char *end = strrchr(personal_dir, PATHSEPCHAR); rubidium@6941: if (end != NULL) end[1] = '\0'; rubidium@6941: _personal_dir = strdup(personal_dir); rubidium@6941: _config_file = str_fmt("%sopenttd.cfg", _personal_dir); rubidium@6941: } else { rubidium@6941: static const Searchpath new_openttd_cfg_order[] = { rubidium@6941: SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR rubidium@6941: }; rubidium@6941: rubidium@6941: for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) { rubidium@6941: if (IsValidSearchPath(new_openttd_cfg_order[i])) { rubidium@6941: _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]); rubidium@6941: _config_file = str_fmt("%sopenttd.cfg", _personal_dir); rubidium@6941: break; rubidium@6941: } rubidium@6941: } rubidium@6941: } rubidium@6929: } rubidium@6298: rubidium@6941: DEBUG(misc, 3, "%s found as personal directory", _personal_dir); rubidium@6298: rubidium@6929: _highscore_file = str_fmt("%shs.dat", _personal_dir); rubidium@6929: _log_file = str_fmt("%sopenttd.log", _personal_dir); rubidium@6298: rubidium@6929: char *save_dir = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(SAVE_DIR)); rubidium@6929: char *autosave_dir = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(AUTOSAVE_DIR)); rubidium@6929: rubidium@6929: /* Make the necessary folders */ rubidium@6929: FioCreateDirectory(_personal_dir); rubidium@6929: FioCreateDirectory(save_dir); rubidium@6929: FioCreateDirectory(autosave_dir); rubidium@6929: rubidium@6929: free(save_dir); rubidium@6929: free(autosave_dir); rubidium@6298: } rubidium@6875: rubidium@6875: /** rubidium@6875: * Sanitizes a filename, i.e. removes all illegal characters from it. rubidium@6875: * @param filename the "\0" terminated filename rubidium@6875: */ rubidium@6875: void SanitizeFilename(char *filename) rubidium@6875: { rubidium@6875: for (; *filename != '\0'; filename++) { rubidium@6875: switch (*filename) { rubidium@6875: /* The following characters are not allowed in filenames rubidium@6875: * on at least one of the supported operating systems: */ rubidium@6875: case ':': case '\\': case '*': case '?': case '/': glx@7016: case '<': case '>': case '|': case '"': rubidium@6875: *filename = '_'; rubidium@6875: break; rubidium@6875: } rubidium@6875: } rubidium@6875: }