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" rubidium@10039: #include "fileio_func.h" tron@2153: #include "variables.h" truelight@5967: #include "debug.h" rubidium@6298: #include "fios.h" rubidium@8130: #include "core/alloc_func.hpp" rubidium@8131: #include "core/math_func.hpp" rubidium@8214: #include "string_func.h" smatz@9581: #include "tar_type.h" rubidium@6929: #ifdef WIN32 rubidium@6929: #include rubidium@6929: #else rubidium@6323: #include rubidium@6317: #include glx@7589: #endif rubidium@6298: #include truelight@0: truelight@0: /*************************************************/ truelight@0: /* FILE IO ROUTINES ******************************/ truelight@0: /*************************************************/ truelight@0: truelight@0: #define FIO_BUFFER_SIZE 512 truelight@0: rubidium@6248: struct Fio { rubidium@7805: byte *buffer, *buffer_end; ///< position pointer in local buffer and last valid byte of buffer glx@9146: size_t pos; ///< current (system) position in file rubidium@7805: FILE *cur_fh; ///< current file handle rubidium@7805: const char *filename; ///< current filename rubidium@7805: FILE *handles[MAX_FILE_SLOTS]; ///< array of file handles we can have open rubidium@7805: byte buffer_start[FIO_BUFFER_SIZE]; ///< local buffer when read from file rubidium@7805: const char *filenames[MAX_FILE_SLOTS]; ///< array of filenames we (should) have open peter1138@8374: char *shortnames[MAX_FILE_SLOTS];///< array of short names for spriteloader's use truelight@5967: #if defined(LIMITED_FDS) rubidium@7805: uint open_handles; ///< current amount of open handles rubidium@7805: uint usage_count[MAX_FILE_SLOTS]; ///< 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@9390: size_t FioGetPos() truelight@0: { rubidium@9103: return _fio.pos + (_fio.buffer - _fio.buffer_end); truelight@0: } truelight@0: peter1138@8374: const char *FioGetFilename(uint8 slot) truelight@6896: { peter1138@8374: return _fio.shortnames[slot]; truelight@6896: } truelight@6896: rubidium@9390: void FioSeekTo(size_t 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 */ smatz@9392: void FioSeekToFile(uint8 slot, size_t 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) { rubidium@9100: _fio.buffer = _fio.buffer_start; rubidium@9103: size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh); rubidium@9103: _fio.pos += size; rubidium@9103: _fio.buffer_end = _fio.buffer_start + size; rubidium@9103: rubidium@9103: if (size == 0) return 0; 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: glx@9146: void FioReadBlock(void *ptr, size_t size) truelight@0: { truelight@0: FioSeekTo(FioGetPos(), SEEK_SET); rubidium@9100: _fio.pos += 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]); peter1138@8374: peter1138@8374: free(_fio.shortnames[slot]); peter1138@8374: _fio.shortnames[slot] = NULL; peter1138@8374: 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); glx@9470: if (f == NULL) usererror("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; peter1138@8374: peter1138@8374: /* Store the filename without path and extension */ peter1138@8374: const char *t = strrchr(filename, PATHSEPCHAR); peter1138@8374: _fio.shortnames[slot] = strdup(t == NULL ? filename : t); peter1138@8374: char *t2 = strrchr(_fio.shortnames[slot], '.'); peter1138@8374: if (t2 != NULL) *t2 = '\0'; peter1138@8374: strtolower(_fio.shortnames[slot]); peter1138@8374: 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@7592: TarList _tar_list; truelight@7592: TarFileList _tar_filelist; rubidium@6929: frosch@9634: typedef std::map TarLinkList; frosch@9634: static TarLinkList _tar_linklist; ///< List of directory links frosch@9634: 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: truelight@7592: FioFCloseFile(f); truelight@7592: return true; truelight@7592: } truelight@7592: truelight@7592: /** truelight@7592: * Close a file in a safe way. truelight@7592: */ truelight@7592: void FioFCloseFile(FILE *f) truelight@7592: { rubidium@6299: fclose(f); 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) { skidd13@10310: strecpy(buf, filename, lastof(buf)); rubidium@6929: } else { rubidium@6929: snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename); rubidium@6929: } rubidium@6929: rubidium@7624: #if defined(WIN32) glx@7628: if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL; rubidium@7624: #endif rubidium@7624: rubidium@6929: f = fopen(buf, mode); bjarni@2736: #if !defined(WIN32) bjarni@2736: if (f == NULL) { rubidium@9084: strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : 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@7592: FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize) truelight@7581: { rubidium@9702: FILE *f = fopen(entry->tar_filename, "rb"); truelight@7581: assert(f != NULL); truelight@7581: truelight@7592: fseek(f, entry->position, SEEK_SET); truelight@7592: if (filesize != NULL) *filesize = entry->size; truelight@7592: return f; 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@7592: 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') { frosch@9634: static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1; // Enough space to hold two filenames plus link. See 'TarHeader'. frosch@9634: char resolved_name[MAX_RESOLVED_LENGTH]; frosch@9634: truelight@7592: /* Filenames in tars are always forced to be lowercase */ frosch@9634: strcpy(resolved_name, filename); frosch@9634: strtolower(resolved_name); frosch@9634: rubidium@9916: size_t resolved_len = strlen(resolved_name); frosch@9634: frosch@9634: /* Resolve ONE directory link */ frosch@9634: for (TarLinkList::iterator link = _tar_linklist.begin(); link != _tar_linklist.end(); link++) { frosch@9634: const std::string &src = link->first; rubidium@9916: size_t len = src.length(); frosch@9635: if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) { frosch@9634: /* Apply link */ frosch@9634: char resolved_name2[MAX_RESOLVED_LENGTH]; frosch@9634: const std::string &dest = link->second; frosch@9634: strcpy(resolved_name2, &(resolved_name[len])); frosch@9634: strcpy(resolved_name, dest.c_str()); frosch@9634: strcpy(&(resolved_name[dest.length()]), resolved_name2); frosch@9634: break; // Only resolve one level frosch@9634: } frosch@9634: } frosch@9634: frosch@9634: TarFileList::iterator it = _tar_filelist.find(resolved_name); truelight@7592: if (it != _tar_filelist.end()) { truelight@7592: f = FioFOpenFileTar(&((*it).second), filesize); truelight@7581: } truelight@7581: } bjarni@2736: rubidium@8009: /* Sometimes a full path is given. To support rubidium@8009: * the 'subdirectory' must be 'removed'. */ rubidium@8009: if (f == NULL && subdir != NO_DIRECTORY) { rubidium@8009: f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize); rubidium@8009: } rubidium@8009: 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@7911: #elif defined(__MORPHOS__) || defined(__AMIGAOS__) rubidium@7911: char buf[MAX_PATH]; rubidium@7911: ttd_strlcpy(buf, name, MAX_PATH); rubidium@7911: rubidium@7911: size_t len = strlen(name) - 1; rubidium@7911: if (buf[len] == '/') { rubidium@7911: buf[len] = '\0'; // Kill pathsep, so mkdir() will not fail rubidium@7911: } rubidium@7911: rubidium@7911: mkdir(OTTD2FS(buf), 0755); 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@9100: if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0'; 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: frosch@9634: /** frosch@9634: * Simplify filenames from tars. frosch@9634: * Replace '/' by PATHSEPCHAR, and force 'name' to lowercase. frosch@9634: * @param name Filename to process. frosch@9634: */ frosch@9634: static void SimplifyFileName(char *name) frosch@9634: { frosch@9634: /* Force lowercase */ frosch@9634: strtolower(name); frosch@9634: frosch@9634: /* Tar-files always have '/' path-seperator, but we want our PATHSEPCHAR */ frosch@9634: #if (PATHSEPCHAR != '/') frosch@9634: for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR; frosch@9634: #endif frosch@9634: } frosch@9634: truelight@7581: static bool TarListAddFile(const char *filename) truelight@7581: { truelight@7592: /* The TAR-header, repeated for every file */ truelight@7592: typedef struct TarHeader { truelight@7592: char name[100]; ///< Name of the file truelight@7592: char mode[8]; truelight@7592: char uid[8]; truelight@7592: char gid[8]; truelight@7592: char size[12]; ///< Size of the file, in ASCII truelight@7592: char mtime[12]; truelight@7592: char chksum[8]; truelight@7592: char typeflag; truelight@7592: char linkname[100]; truelight@7592: char magic[6]; truelight@7592: char version[2]; truelight@7592: char uname[32]; truelight@7592: char gname[32]; truelight@7592: char devmajor[8]; truelight@7592: char devminor[8]; truelight@7592: char prefix[155]; ///< Path of the file truelight@7592: truelight@7592: char unused[12]; truelight@7592: } TarHeader; truelight@7592: truelight@7592: /* Check if we already seen this file */ truelight@7592: TarList::iterator it = _tar_list.find(filename); truelight@7592: if (it != _tar_list.end()) return false; truelight@7592: truelight@7592: FILE *f = fopen(filename, "rb"); truelight@7592: assert(f != NULL); truelight@7592: rubidium@9702: const char *dupped_filename = strdup(filename); rubidium@9702: _tar_list[filename].filename = dupped_filename; truelight@7592: frosch@9634: TarLinkList links; ///< Temporary list to collect links frosch@9634: truelight@7592: TarHeader th; truelight@7592: char buf[sizeof(th.name) + 1], *end; truelight@7592: char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1]; frosch@9634: char link[sizeof(th.linkname) + 1]; frosch@9634: char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1]; glx@9146: size_t num = 0, pos = 0; truelight@7592: truelight@7592: /* Make a char of 512 empty bytes */ truelight@7592: char empty[512]; truelight@7592: memset(&empty[0], 0, sizeof(empty)); truelight@7592: frosch@9630: for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it? frosch@9630: size_t num_bytes_read = fread(&th, 1, 512, f); frosch@9630: if (num_bytes_read != 512) break; frosch@9630: pos += num_bytes_read; truelight@7592: truelight@7592: /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */ truelight@7592: if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) { truelight@7592: /* If we have only zeros in the block, it can be an end-of-file indicator */ truelight@7592: if (memcmp(&th, &empty[0], 512) == 0) continue; truelight@7592: truelight@7592: DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename); truelight@7592: return false; truelight@7592: } truelight@7592: truelight@7592: name[0] = '\0'; glx@9146: size_t len = 0; truelight@7592: truelight@7592: /* The prefix contains the directory-name */ truelight@7592: if (th.prefix[0] != '\0') { truelight@7592: memcpy(name, th.prefix, sizeof(th.prefix)); truelight@7592: name[sizeof(th.prefix)] = '\0'; truelight@7592: len = strlen(name); truelight@7592: name[len] = PATHSEPCHAR; truelight@7592: len++; truelight@7592: } truelight@7592: truelight@7592: /* Copy the name of the file in a safe way at the end of 'name' */ truelight@7592: memcpy(&name[len], th.name, sizeof(th.name)); truelight@7592: name[len + sizeof(th.name)] = '\0'; truelight@7592: truelight@7592: /* Calculate the size of the file.. for some strange reason this is stored as a string */ truelight@7592: memcpy(buf, th.size, sizeof(th.size)); truelight@7592: buf[sizeof(th.size)] = '\0'; truelight@7592: int skip = strtol(buf, &end, 8); truelight@7592: frosch@9634: switch (th.typeflag) { frosch@9634: case '\0': frosch@9634: case '0': { // regular file frosch@9634: /* Ignore empty files */ frosch@9634: if (skip == 0) break; truelight@7592: frosch@9634: if (strlen(name) == 0) break; truelight@7592: frosch@9634: /* Store this entry in the list */ frosch@9634: TarFileListEntry entry; rubidium@9702: entry.tar_filename = dupped_filename; rubidium@9702: entry.size = skip; rubidium@9702: entry.position = pos; truelight@7592: frosch@9634: /* Convert to lowercase and our PATHSEPCHAR */ frosch@9634: SimplifyFileName(name); frosch@9634: frosch@9634: DEBUG(misc, 6, "Found file in tar: %s (%d bytes, %d offset)", name, skip, pos); frosch@9634: if (_tar_filelist.insert(TarFileList::value_type(name, entry)).second) num++; frosch@9634: frosch@9634: break; frosch@9634: } frosch@9634: frosch@9634: case '1': // hard links frosch@9634: case '2': { // symbolic links frosch@9634: /* Copy the destination of the link in a safe way at the end of 'linkname' */ frosch@9634: memcpy(link, th.linkname, sizeof(th.linkname)); frosch@9634: link[sizeof(th.linkname)] = '\0'; frosch@9634: frosch@9634: if (strlen(name) == 0 || strlen(link) == 0) break; frosch@9634: frosch@9634: /* Convert to lowercase and our PATHSEPCHAR */ frosch@9634: SimplifyFileName(name); frosch@9634: SimplifyFileName(link); frosch@9634: frosch@9634: /* Only allow relative links */ frosch@9634: if (link[0] == PATHSEPCHAR) { frosch@9634: DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link); frosch@9634: break; frosch@9634: } frosch@9634: frosch@9634: /* Process relative path. frosch@9634: * Note: The destination of links must not contain any directory-links. */ frosch@9634: strcpy(dest, name); frosch@9634: char *destpos = strrchr(dest, PATHSEPCHAR); frosch@9634: if (destpos == NULL) destpos = dest; frosch@9634: *destpos = '\0'; frosch@9634: frosch@9634: char *pos = link; frosch@9634: while (*pos != '\0') { frosch@9634: char *next = strchr(link, PATHSEPCHAR); frosch@9634: if (next == NULL) next = pos + strlen(pos); frosch@9634: frosch@9634: /* Skip '.' (current dir) */ frosch@9634: if (next != pos + 1 || pos[0] != '.') { frosch@9634: if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') { frosch@9634: /* level up */ frosch@9634: if (dest[0] == '\0') { frosch@9634: DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link); frosch@9634: break; frosch@9634: } frosch@9634: frosch@9634: /* Truncate 'dest' after last PATHSEPCHAR. frosch@9634: * This assumes, that the truncated part is a real directory and not a link */ frosch@9634: destpos = strrchr(dest, PATHSEPCHAR); frosch@9634: if (destpos == NULL) destpos = dest; frosch@9634: } else { frosch@9634: /* Append at end of 'dest' */ frosch@9634: if (destpos != dest) *(destpos++) = PATHSEPCHAR; rubidium@10201: strncpy(destpos, pos, next - pos); // Safe as we do '\0'-termination ourselves frosch@9634: destpos += next - pos; frosch@9634: } frosch@9634: *destpos = '\0'; frosch@9634: } frosch@9634: frosch@9634: pos = next; frosch@9634: } frosch@9634: frosch@9634: /* Store links in temporary list */ frosch@9634: DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest); frosch@9634: links.insert(TarLinkList::value_type(name, dest)); frosch@9634: frosch@9634: break; frosch@9634: } frosch@9634: frosch@9634: default: frosch@9634: /* Ignore other types */ frosch@9634: break; frosch@9634: } truelight@7592: truelight@7592: /* Skip to the next block.. */ skidd13@7927: skip = Align(skip, 512); truelight@7592: fseek(f, skip, SEEK_CUR); truelight@7592: pos += skip; truelight@7581: } truelight@7581: truelight@7592: DEBUG(misc, 1, "Found tar '%s' with %d new files", filename, num); truelight@7592: fclose(f); truelight@7581: frosch@9634: /* Resolve file links and store directory links. frosch@9634: * We restrict usage of links to two cases: frosch@9634: * 1) Links to directories: frosch@9634: * Both the source path and the destination path must NOT contain any further links. frosch@9634: * When resolving files at most one directory link is resolved. frosch@9634: * 2) Links to files: frosch@9634: * The destination path must NOT contain any links. frosch@9634: * The source path may contain one directory link. frosch@9634: */ frosch@9634: for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) { frosch@9634: const std::string &src = link->first; frosch@9634: const std::string &dest = link->second; frosch@9634: frosch@9634: TarFileList::iterator dest_file = _tar_filelist.find(dest); frosch@9634: if (dest_file != _tar_filelist.end()) { frosch@9634: /* Link to file. Process the link like the destination file. */ frosch@9634: _tar_filelist.insert(TarFileList::value_type(src, dest_file->second)); frosch@9634: } else { frosch@9634: /* Destination file not found. Assume 'link to directory' */ frosch@9634: /* Append PATHSEPCHAR to 'src' and 'dest' */ frosch@9634: const std::string src_path = src + PATHSEPCHAR; frosch@9634: const std::string dst_path = (dest.length() == 0 ? "" : dest + PATHSEPCHAR); frosch@9634: _tar_linklist.insert(TarLinkList::value_type(src_path, dst_path)); frosch@9634: } frosch@9634: } frosch@9634: truelight@7581: return true; truelight@7581: } truelight@7581: glx@9146: static int ScanPathForTarFiles(const char *path, size_t 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: glx@7589: 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: glx@7589: #if defined(WIN32) || defined(WINCE) glx@7589: /** glx@7589: * Determine the base (personal dir and game data dir) paths glx@7589: * @param exe the path from the current path to the executable glx@7589: * @note defined in the OS related files (os2.cpp, win32.cpp, unix.cpp etc) glx@7589: */ glx@7589: extern void DetermineBasePaths(const char *exe); glx@7589: #else /* defined(WIN32) || defined(WINCE) */ glx@7589: glx@7589: /** glx@7589: * Changes the working directory to the path of the give executable. glx@7589: * For OSX application bundles '.app' is the required extension of the bundle, glx@7589: * so when we crop the path to there, when can remove the name of the bundle glx@7589: * in the same way we remove the name from the executable name. glx@7589: * @param exe the path to the executable glx@7589: */ glx@7589: void ChangeWorkingDirectory(const char *exe) glx@7589: { glx@7589: #ifdef WITH_COCOA glx@7589: char *app_bundle = strchr(exe, '.'); glx@7589: while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.'); glx@7589: glx@7589: if (app_bundle != NULL) app_bundle[0] = '\0'; glx@7589: #endif /* WITH_COCOA */ glx@7589: char *s = strrchr(exe, PATHSEPCHAR); glx@7589: if (s != NULL) { glx@7589: *s = '\0'; rubidium@10385: #if defined(__DJGPP__) rubidium@10385: /* If we want to go to the root, we can't use cd C:, but we must use '/' */ rubidium@10385: if (s[-1] == ':') chdir("/"); rubidium@10385: #endif rubidium@9100: if (chdir(exe) != 0) DEBUG(misc, 0, "Directory with the binary does not exist?"); glx@7589: *s = PATHSEPCHAR; glx@7589: } glx@7589: #ifdef WITH_COCOA glx@7589: if (app_bundle != NULL) app_bundle[0] = '.'; glx@7589: #endif /* WITH_COCOA */ glx@7589: } glx@7589: 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@10385: #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || !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 bjarni@8273: bjarni@8273: #if defined(WITH_SHARED_DIR) bjarni@8273: snprintf(tmp, MAX_PATH, "%s", SHARED_DIR); bjarni@8273: AppendPathSeparator(tmp, MAX_PATH); bjarni@8273: _searchpaths[SP_SHARED_DIR] = strdup(tmp); bjarni@8273: #else rubidium@6929: _searchpaths[SP_SHARED_DIR] = NULL; bjarni@8273: #endif rubidium@6929: rubidium@6990: #if defined(__MORPHOS__) || defined(__AMIGA__) rubidium@6990: _searchpaths[SP_WORKING_DIR] = NULL; rubidium@6990: #else rubidium@9100: if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0'; 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@9100: if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0'; rubidium@6929: AppendPathSeparator(tmp, MAX_PATH); rubidium@6929: _searchpaths[SP_BINARY_DIR] = strdup(tmp); rubidium@6929: rubidium@10385: #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) 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@7911: #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR) rubidium@6929: FioCreateDirectory(_personal_dir); rubidium@7911: #endif rubidium@7911: 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: } rubidium@8131: rubidium@8131: void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize) rubidium@8131: { rubidium@8131: FILE *in; rubidium@8131: byte *mem; rubidium@8131: size_t len; rubidium@8131: rubidium@8131: in = fopen(filename, "rb"); rubidium@8131: if (in == NULL) return NULL; rubidium@8131: rubidium@8131: fseek(in, 0, SEEK_END); rubidium@8131: len = ftell(in); rubidium@8131: fseek(in, 0, SEEK_SET); rubidium@8131: if (len > maxsize || (mem = MallocT(len + 1)) == NULL) { rubidium@8131: fclose(in); rubidium@8131: return NULL; rubidium@8131: } rubidium@8131: mem[len] = 0; rubidium@8131: if (fread(mem, len, 1, in) != 1) { rubidium@8131: fclose(in); rubidium@8131: free(mem); rubidium@8131: return NULL; rubidium@8131: } rubidium@8131: fclose(in); rubidium@8131: rubidium@8131: *lenp = len; rubidium@8131: return mem; rubidium@8131: } rubidium@10036: rubidium@10036: rubidium@10036: /** rubidium@10036: * Scan a single directory (and recursively it's children) and add rubidium@10036: * any graphics sets that are found. rubidium@10036: * @param extension the extension of files to search for. rubidium@10036: * @param path full path we're currently at rubidium@10036: * @param basepath_length from where in the path are we 'based' on the search path rubidium@10036: */ rubidium@10036: static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length) rubidium@10036: { rubidium@10036: extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb); rubidium@10036: rubidium@10036: uint num = 0; rubidium@10036: struct stat sb; rubidium@10036: struct dirent *dirent; rubidium@10036: DIR *dir; rubidium@10036: rubidium@10036: if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0; rubidium@10036: rubidium@10036: while ((dirent = readdir(dir)) != NULL) { rubidium@10036: const char *d_name = FS2OTTD(dirent->d_name); rubidium@10036: char filename[MAX_PATH]; rubidium@10036: rubidium@10036: if (!FiosIsValidFile(path, dirent, &sb)) continue; rubidium@10036: rubidium@10036: snprintf(filename, lengthof(filename), "%s%s", path, d_name); rubidium@10036: rubidium@10384: if (S_ISDIR(sb.st_mode)) { rubidium@10036: /* Directory */ rubidium@10036: if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue; rubidium@10036: AppendPathSeparator(filename, lengthof(filename)); rubidium@10036: num += ScanPath(fs, extension, filename, basepath_length); rubidium@10384: } else if (S_ISREG(sb.st_mode)) { rubidium@10036: /* File */ rubidium@10036: char *ext = strrchr(filename, '.'); rubidium@10036: rubidium@10036: /* If no extension or extension isn't .grf, skip the file */ rubidium@10036: if (ext == NULL) continue; rubidium@10036: if (strcasecmp(ext, extension) != 0) continue; rubidium@10036: rubidium@10036: if (fs->AddFile(filename, basepath_length)) num++; rubidium@10036: } rubidium@10036: } rubidium@10036: rubidium@10036: closedir(dir); rubidium@10036: rubidium@10036: return num; rubidium@10036: } rubidium@10036: rubidium@10036: /** rubidium@10036: * Scan the given tar and add graphics sets when it finds one. rubidium@10036: * @param extension the extension of files to search for. rubidium@10036: * @param tar the tar to search in. rubidium@10036: */ rubidium@10036: static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar) rubidium@10036: { rubidium@10036: uint num = 0; rubidium@10036: const char *filename = (*tar).first.c_str(); rubidium@10036: const char *ext = strrchr(filename, '.'); rubidium@10036: rubidium@10036: /* If no extension or extension isn't .grf, skip the file */ rubidium@10036: if (ext == NULL) return false; rubidium@10036: if (strcasecmp(ext, extension) != 0) return false; rubidium@10036: rubidium@10036: if (fs->AddFile(filename, 0)) num++; rubidium@10036: rubidium@10036: return num; rubidium@10036: } rubidium@10036: rubidium@10036: /** rubidium@10036: * Scan for files with the given extention in the given search path. rubidium@10036: * @param extension the extension of files to search for. rubidium@10238: * @param sd the sub directory to search in. rubidium@10036: * @param tars whether to search in the tars too. rubidium@10036: * @return the number of found files, i.e. the number of times that rubidium@10036: * AddFile returned true. rubidium@10036: */ rubidium@10036: uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars) rubidium@10036: { rubidium@10036: Searchpath sp; rubidium@10036: char path[MAX_PATH]; rubidium@10036: TarFileList::iterator tar; rubidium@10036: uint num = 0; rubidium@10036: rubidium@10036: FOR_ALL_SEARCHPATHS(sp) { rubidium@10036: FioAppendDirectory(path, MAX_PATH, sp, sd); rubidium@10036: num += ScanPath(this, extension, path, strlen(path)); rubidium@10036: } rubidium@10036: FOR_ALL_TARS(tar) { rubidium@10036: num += ScanTar(this, extension, tar); rubidium@10036: } rubidium@10036: rubidium@10036: return num; rubidium@10036: }