20 # define access _taccess
21 #elif defined(__HAIKU__)
23 #include <storage/FindDirectory.h>
31 #ifdef WITH_XDG_BASEDIR
38 #define FIO_BUFFER_SIZE 512
50 #if defined(LIMITED_FDS)
93 if (fseek(_fio.
cur_fh, _fio.
pos, SEEK_SET) < 0) {
98 #if defined(LIMITED_FDS)
99 static void FioRestoreFile(
int slot)
102 if (_fio.
handles[slot] == NULL) {
103 DEBUG(misc, 6,
"Restoring file '%s' in slot '%d' from disk", _fio.
filenames[slot], slot);
106 _fio.usage_count[slot]++;
118 #if defined(LIMITED_FDS)
120 FioRestoreFile(slot);
141 if (size == 0)
return 0;
143 return *_fio.buffer++;
190 _fio.
pos += fread(ptr, 1, size, _fio.
cur_fh);
199 if (_fio.
handles[slot] != NULL) {
206 #if defined(LIMITED_FDS)
220 #if defined(LIMITED_FDS)
221 static void FioFreeHandle()
224 if (_fio.open_handles + 1 == LIMITED_FDS) {
232 if (_fio.
handles[i] != NULL && _fio.usage_count[i] < count) {
233 count = _fio.usage_count[i];
238 DEBUG(misc, 6,
"Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.
filenames[slot], slot);
254 #if defined(LIMITED_FDS)
258 if (f == NULL)
usererror(
"Cannot open file '%s'", filename);
260 if (pos < 0)
usererror(
"Cannot read file '%s'", filename);
267 const char *t = strrchr(filename, PATHSEPCHAR);
269 char *t2 = strrchr(_fio.
shortnames[slot],
'.');
270 if (t2 != NULL) *t2 =
'\0';
273 #if defined(LIMITED_FDS)
274 _fio.usage_count[slot] = 0;
280 static const char *
const _subdirs[] = {
283 "save" PATHSEP
"autosave" PATHSEP,
285 "scenario" PATHSEP
"heightmap" PATHSEP,
292 "ai" PATHSEP
"library" PATHSEP,
294 "game" PATHSEP
"library" PATHSEP,
295 "screenshot" PATHSEP,
303 typedef std::map<std::string, std::string> TarLinkList;
315 if (f == NULL)
return false;
330 HANDLE hand = CreateFile(
OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
331 if (hand == INVALID_HANDLE_VALUE)
return 1;
335 return access(
OTTD2FS(filename), 0) == 0;
347 char *FioGetFullPath(
char *buf,
const char *last,
Searchpath sp,
Subdirectory subdir,
const char *filename)
350 assert(sp < NUM_SEARCHPATHS);
352 seprintf(buf, last,
"%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
370 FioGetFullPath(buf, last, sp, subdir, filename);
386 assert(sp < NUM_SEARCHPATHS);
388 seprintf(buf, last,
"%s%s", _searchpaths[sp], _subdirs[subdir]);
392 char *FioGetDirectory(
char *buf,
const char *last,
Subdirectory subdir)
398 char *ret = FioAppendDirectory(buf, last, sp, subdir);
408 static FILE *FioFOpenFileSp(
const char *filename,
const char *mode,
Searchpath sp,
Subdirectory subdir,
size_t *filesize)
410 #if defined(WIN32) && defined(UNICODE)
416 MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode,
lengthof(Lmode));
424 seprintf(buf,
lastof(buf),
"%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
428 if (mode[0] ==
'r' && GetFileAttributes(
OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES)
return NULL;
431 f = fopen(buf, mode);
434 f = fopen(buf, mode);
437 if (f != NULL && filesize != NULL) {
439 fseek(f, 0, SEEK_END);
440 *filesize = ftell(f);
441 fseek(f, 0, SEEK_SET);
455 FILE *f = fopen(entry->tar_filename,
"rb");
456 if (f == NULL)
return f;
458 if (fseek(f, entry->position, SEEK_SET) < 0) {
463 if (filesize != NULL) *filesize = entry->size;
482 f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
487 if (f == NULL && mode[0] ==
'r' && subdir !=
NO_DIRECTORY) {
488 static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1;
489 char resolved_name[MAX_RESOLVED_LENGTH];
495 size_t resolved_len = strlen(resolved_name);
498 for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
499 const std::string &src = link->first;
500 size_t len = src.length();
501 if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
503 char resolved_name2[MAX_RESOLVED_LENGTH];
504 const std::string &dest = link->second;
505 strecpy(resolved_name2, &(resolved_name[len]),
lastof(resolved_name2));
507 strecpy(&(resolved_name[dest.length()]), resolved_name2,
lastof(resolved_name));
512 TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
513 if (it != _tar_filelist[subdir].end()) {
524 if (f != NULL)
break;
547 #if defined(WIN32) || defined(WINCE)
548 CreateDirectory(
OTTD2FS(name), NULL);
549 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
551 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
555 size_t len = strlen(name) - 1;
556 if (buf[len] ==
'/') {
575 size_t s = strlen(buf);
578 if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
579 if (&buf[s] >= last)
return false;
581 seprintf(buf + s, last,
"%c", PATHSEPCHAR);
595 char *dest = MallocT<char>(MAX_PATH);
596 char *last = dest + MAX_PATH - 1;
600 const char *s = strchr(dest, PATHSEPCHAR);
603 if (s == NULL || dest != s) {
604 if (getcwd(dest, MAX_PATH) == NULL) *dest =
'\0';
620 TarList::iterator it = _tar_list[subdir].find(tarname);
621 if (it == _tar_list[subdir].end())
return NULL;
622 return (*it).second.dirname;
625 static void TarAddLink(
const std::string &srcParam,
const std::string &destParam,
Subdirectory subdir)
627 std::string src = srcParam;
628 std::string dest = destParam;
630 std::transform(src.begin(), src.end(), src.begin(), tolower);
631 std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
633 TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
634 if (dest_file != _tar_filelist[subdir].end()) {
636 _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
640 const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
641 const std::string dst_path = (dest.length() == 0 ?
"" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
642 _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
646 void FioTarAddLink(
const char *src,
const char *dest,
Subdirectory subdir)
648 TarAddLink(src, dest, subdir);
662 #if (PATHSEPCHAR != '/')
663 for (
char *n = name; *n !=
'\0'; n++)
if (*n ==
'/') *n = PATHSEPCHAR;
674 _tar_filelist[sd].clear();
675 _tar_list[sd].clear();
676 uint num = this->
Scan(
".tar", sd,
false);
683 DEBUG(misc, 1,
"Scanning for tars");
704 DEBUG(misc, 1,
"Scan complete, found %d files", num);
717 return this->
AddFile(filename, 0);
723 assert(tar_filename == NULL);
748 TarList::iterator it = _tar_list[this->
subdir].find(filename);
749 if (it != _tar_list[this->subdir].end())
return false;
751 FILE *f = fopen(filename,
"rb");
756 if (f == NULL)
return false;
758 const char *dupped_filename =
stredup(filename);
759 _tar_list[this->
subdir][filename].filename = dupped_filename;
760 _tar_list[this->
subdir][filename].dirname = NULL;
765 char buf[
sizeof(th.name) + 1], *end;
766 char name[
sizeof(th.prefix) + 1 +
sizeof(th.name) + 1];
767 char link[
sizeof(th.linkname) + 1];
768 char dest[
sizeof(th.prefix) + 1 +
sizeof(th.name) + 1 + 1 +
sizeof(th.linkname) + 1];
769 size_t num = 0, pos = 0;
773 memset(&empty[0], 0,
sizeof(empty));
776 size_t num_bytes_read = fread(&th, 1, 512, f);
777 if (num_bytes_read != 512)
break;
778 pos += num_bytes_read;
781 if (strncmp(th.magic,
"ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
783 if (memcmp(&th, &empty[0], 512) == 0)
continue;
785 DEBUG(misc, 0,
"The file '%s' isn't a valid tar-file", filename);
793 if (th.prefix[0] !=
'\0') {
803 size_t skip = strtoul(buf, &end, 8);
805 switch (th.typeflag) {
809 if (skip == 0)
break;
811 if (strlen(name) == 0)
break;
815 entry.tar_filename = dupped_filename;
817 entry.position = pos;
822 DEBUG(misc, 6,
"Found file in tar: %s (" PRINTF_SIZE
" bytes, " PRINTF_SIZE
" offset)", name, skip, pos);
823 if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
833 if (strlen(name) == 0 || strlen(link) == 0)
break;
840 if (link[0] == PATHSEPCHAR) {
841 DEBUG(misc, 1,
"Ignoring absolute link in tar: %s -> %s", name, link);
848 char *destpos = strrchr(dest, PATHSEPCHAR);
849 if (destpos == NULL) destpos = dest;
853 while (*pos !=
'\0') {
854 char *next = strchr(pos, PATHSEPCHAR);
856 next = pos + strlen(pos);
862 if (strcmp(pos,
".") == 0) {
864 }
else if (strcmp(pos,
"..") == 0) {
866 if (dest[0] ==
'\0') {
867 DEBUG(misc, 1,
"Ignoring link pointing outside of data directory: %s -> %s", name, link);
873 destpos = strrchr(dest, PATHSEPCHAR);
874 if (destpos == NULL) destpos = dest;
878 if (destpos != dest) destpos =
strecpy(destpos, PATHSEP,
lastof(dest));
882 if (destpos >=
lastof(dest)) {
883 DEBUG(misc, 0,
"The length of a link in tar-file '%s' is too large (malformed?)", filename);
892 DEBUG(misc, 6,
"Found link in tar: %s -> %s", name, dest);
893 links.insert(TarLinkList::value_type(name, dest));
903 DEBUG(misc, 6,
"Found dir in tar: %s", name);
904 if (_tar_list[this->subdir][filename].dirname == NULL) _tar_list[this->
subdir][filename].dirname =
stredup(name);
913 skip =
Align(skip, 512);
914 if (fseek(f, skip, SEEK_CUR) < 0) {
915 DEBUG(misc, 0,
"The file '%s' can't be read as a valid tar-file", filename);
922 DEBUG(misc, 1,
"Found tar '%s' with " PRINTF_SIZE
" new files", filename, num);
934 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
935 const std::string &src = link->first;
936 const std::string &dest = link->second;
937 TarAddLink(src, dest, this->subdir);
952 TarList::iterator it = _tar_list[subdir].find(tar_filename);
954 if (it == _tar_list[subdir].end())
return false;
956 const char *dirname = (*it).second.dirname;
959 if (dirname == NULL)
return false;
961 char filename[MAX_PATH];
963 char *p = strrchr(filename, PATHSEPCHAR);
965 if (p == NULL)
return false;
969 DEBUG(misc, 8,
"Extracting %s to directory %s", tar_filename, filename);
972 for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
973 if (strcmp((*it2).second.tar_filename, tar_filename) != 0)
continue;
977 DEBUG(misc, 9,
" extracting %s", filename);
983 DEBUG(misc, 6,
"Extracting %s failed; could not open %s", filename, tar_filename);
988 FILE *out = fopen(filename,
"wb");
990 DEBUG(misc, 6,
"Extracting %s failed; could not open %s", filename, filename);
998 for (; to_copy != 0; to_copy -= read) {
999 read = fread(buffer, 1,
min(to_copy,
lengthof(buffer)), in);
1000 if (read <= 0 || fwrite(buffer, 1, read, out) != read)
break;
1008 DEBUG(misc, 6,
"Extracting %s failed; still %i bytes to copy", filename, (
int)to_copy);
1013 DEBUG(misc, 9,
" extraction successful");
1017 #if defined(WIN32) || defined(WINCE)
1035 bool success =
false;
1037 char *app_bundle = strchr(exe,
'.');
1038 while (app_bundle != NULL && strncasecmp(app_bundle,
".app", 4) != 0) app_bundle = strchr(&app_bundle[1],
'.');
1040 if (app_bundle != NULL) app_bundle[0] =
'\0';
1042 char *s =
const_cast<char *
>(strrchr(exe, PATHSEPCHAR));
1045 #if defined(__DJGPP__)
1047 if (s[-1] ==
':') chdir(
"/");
1049 if (chdir(exe) != 0) {
1050 DEBUG(misc, 0,
"Directory with the binary does not exist?");
1057 if (app_bundle != NULL) app_bundle[0] =
'.';
1078 if (strcmp(_searchpaths[
SP_WORKING_DIR], PATHSEP) == 0)
return false;
1084 seprintf(tmp,
lastof(tmp),
"%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
1096 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1097 const char *xdg_data_home = xdgDataHome(NULL);
1099 PERSONAL_DIR[0] ==
'.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1100 free(xdg_data_home);
1103 _searchpaths[SP_PERSONAL_DIR_XDG] =
stredup(tmp);
1105 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
1110 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
1111 const char *homedir =
stredup(path.Path());
1117 const char *homedir = getenv(
"HOME");
1118 if (homedir != NULL) {
1122 if (homedir == NULL) {
1123 const struct passwd *pw = getpwuid(getuid());
1124 homedir = (pw == NULL) ? NULL :
stredup(pw->pw_dir);
1128 if (homedir != NULL) {
1130 seprintf(tmp,
lastof(tmp),
"%s" PATHSEP
"%s", homedir, PERSONAL_DIR);
1140 #if defined(WITH_SHARED_DIR)
1148 #if defined(__MORPHOS__) || defined(__AMIGA__)
1151 if (getcwd(tmp, MAX_PATH) == NULL) *tmp =
'\0';
1160 if (getcwd(tmp, MAX_PATH) == NULL) *tmp =
'\0';
1170 DEBUG(misc, 0,
"Failed to return to working directory!");
1174 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
1182 extern void cocoaSetApplicationBundleDir();
1183 cocoaSetApplicationBundleDir();
1202 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1203 char config_home[MAX_PATH];
1205 const char *xdg_config_home = xdgConfigHome(NULL);
1206 seprintf(config_home,
lastof(config_home),
"%s" PATHSEP
"%s", xdg_config_home,
1207 PERSONAL_DIR[0] ==
'.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1208 free(xdg_config_home);
1216 DEBUG(misc, 4,
"%s added as search path", _searchpaths[sp]);
1222 char *end = strrchr(config_dir, PATHSEPCHAR);
1224 config_dir[0] =
'\0';
1229 char personal_dir[MAX_PATH];
1231 char *end = strrchr(personal_dir, PATHSEPCHAR);
1232 if (end != NULL) end[1] =
'\0';
1233 config_dir =
stredup(personal_dir);
1236 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1238 config_dir = config_home;
1240 static const Searchpath new_openttd_cfg_order[] = {
1245 for (uint i = 0; i <
lengthof(new_openttd_cfg_order); i++) {
1247 config_dir =
stredup(_searchpaths[new_openttd_cfg_order[i]]);
1251 assert(config_dir != NULL);
1257 DEBUG(misc, 3,
"%s found as config directory", config_dir);
1260 extern char *_hotkeys_file;
1261 _hotkeys_file =
str_fmt(
"%shotkeys.cfg", config_dir);
1263 _windows_file =
str_fmt(
"%swindows.cfg", config_dir);
1265 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1266 if (config_dir == config_home) {
1269 _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1274 _personal_dir = config_dir;
1278 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
1283 DEBUG(misc, 3,
"%s found as personal directory", _personal_dir);
1286 SAVE_DIR,
AUTOSAVE_DIR,
SCENARIO_DIR,
HEIGHTMAP_DIR,
BASESET_DIR,
NEWGRF_DIR,
AI_DIR,
AI_LIBRARY_DIR,
GAME_DIR,
GAME_LIBRARY_DIR,
SCREENSHOT_DIR
1289 for (uint i = 0; i <
lengthof(default_subdirs); i++) {
1290 char *dir =
str_fmt(
"%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
1297 #ifdef ENABLE_NETWORK
1302 for (uint i = 0; i <
lengthof(dirs); i++) {
1303 char *tmp =
str_fmt(
"%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
1309 _log_file =
str_fmt(
"%sopenttd.log", _personal_dir);
1313 if (!
FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
1314 free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
1326 for (; *filename !=
'\0'; filename++) {
1327 switch (*filename) {
1330 case ':':
case '\\':
case '*':
case '?':
case '/':
1331 case '<':
case '>':
case '|':
case '"':
1348 FILE *in = fopen(filename,
"rb");
1349 if (in == NULL)
return NULL;
1351 fseek(in, 0, SEEK_END);
1352 size_t len = ftell(in);
1353 fseek(in, 0, SEEK_SET);
1354 if (len > maxsize) {
1358 byte *mem = MallocT<byte>(len + 1);
1360 if (fread(mem, len, 1, in) != 1) {
1379 if (extension == NULL)
return true;
1381 const char *ext = strrchr(filename, extension[0]);
1382 return ext != NULL && strcasecmp(ext, extension) == 0;
1394 static uint
ScanPath(
FileScanner *fs,
const char *extension,
const char *path,
size_t basepath_length,
bool recursive)
1396 extern bool FiosIsValidFile(
const char *path,
const struct dirent *ent,
struct stat *sb);
1400 struct dirent *dirent;
1403 if (path == NULL || (dir =
ttd_opendir(path)) == NULL)
return 0;
1405 while ((dirent = readdir(dir)) != NULL) {
1406 const char *d_name =
FS2OTTD(dirent->d_name);
1407 char filename[MAX_PATH];
1409 if (!FiosIsValidFile(path, dirent, &sb))
continue;
1413 if (S_ISDIR(sb.st_mode)) {
1415 if (!recursive)
continue;
1416 if (strcmp(d_name,
".") == 0 || strcmp(d_name,
"..") == 0)
continue;
1418 num +=
ScanPath(fs, extension, filename, basepath_length, recursive);
1419 }
else if (S_ISREG(sb.st_mode)) {
1439 const char *filename = (*tar).first.c_str();
1460 char path[MAX_PATH];
1461 TarFileList::iterator tar;
1468 FioAppendDirectory(path,
lastof(path), sp, sd);
1469 num +=
ScanPath(
this, extension, path, strlen(path), recursive);
1473 FOR_ALL_TARS(tar, sd) {
1474 num +=
ScanTar(
this, extension, tar);
1502 char path[MAX_PATH];
1505 return ScanPath(
this, extension, path, strlen(path), recursive);