OpenTTD Source: src/fileio.cpp Source File

OpenTTD Source 12.2

fileio.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10 #include "stdafx.h"
11 #include "fileio_func.h"
12 #include "spriteloader/spriteloader.hpp"
13 #include "debug.h"
14 #include "fios.h"
15 #include "string_func.h"
16 #include "tar_type.h"
17 #ifdef _WIN32
18 #include <windows.h>
19 # define access _taccess
20 #elif defined(__HAIKU__)
21 #include <Path.h>
22 #include <storage/FindDirectory.h>
23 #else
24 #include <unistd.h>
25 #include <pwd.h>
26 #endif
27 #include <sys/stat.h>
28 #include <array>
29 #include <sstream>
30
31 #include "safeguards.h"
32
34 static bool _do_scan_working_directory = true;
35
36 extern std::string _config_file;
37 extern std::string _highscore_file;
38
39 static const char * const _subdirs[] = {
40 "",
41 "save" PATHSEP,
42 "save" PATHSEP "autosave" PATHSEP,
43 "scenario" PATHSEP,
44 "scenario" PATHSEP "heightmap" PATHSEP,
45 "gm" PATHSEP,
46 "data" PATHSEP,
47 "baseset" PATHSEP,
48 "newgrf" PATHSEP,
49 "lang" PATHSEP,
50 "ai" PATHSEP,
51 "ai" PATHSEP "library" PATHSEP,
52 "game" PATHSEP,
53 "game" PATHSEP "library" PATHSEP,
54 "screenshot" PATHSEP,
55 };
56 static_assert(lengthof(_subdirs) == NUM_SUBDIRS);
57
64 std::array<std::string, NUM_SEARCHPATHS> _searchpaths;
65 std::vector<Searchpath> _valid_searchpaths;
66 std::array<TarList, NUM_SUBDIRS> _tar_list;
67 TarFileList _tar_filelist[NUM_SUBDIRS];
68
69 typedef std::map<std::string, std::string> TarLinkList;
70 static TarLinkList _tar_linklist[NUM_SUBDIRS];
71
77 static bool IsValidSearchPath(Searchpath sp)
78 {
79 return sp < _searchpaths.size() && !_searchpaths[sp].empty();
80 }
81
82 static void FillValidSearchPaths(bool only_local_path)
83 {
84 _valid_searchpaths.clear();
85 for (Searchpath sp = SP_FIRST_DIR; sp < NUM_SEARCHPATHS; sp++) {
86 if (only_local_path) {
87 switch (sp) {
88 case SP_WORKING_DIR: // Can be influence by "-c" option.
89 case SP_BINARY_DIR: // Most likely contains all the language files.
90 case SP_AUTODOWNLOAD_DIR: // Otherwise we cannot download in-game content.
91 break;
92
93 default:
94 continue;
95 }
96 }
97
98 if (IsValidSearchPath(sp)) _valid_searchpaths.emplace_back(sp);
99 }
100 }
101
108 bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
109 {
110 FILE *f = FioFOpenFile(filename, "rb", subdir);
111 if (f == nullptr) return false;
112
113 FioFCloseFile(f);
114 return true;
115 }
116
122 bool FileExists(const std::string &filename)
123 {
124 return access(OTTD2FS(filename).c_str(), 0) == 0;
125 }
126
130 void FioFCloseFile(FILE *f)
131 {
132 fclose(f);
133 }
134
141 std::string FioFindFullPath(Subdirectory subdir, const char *filename)
142 {
143 assert(subdir < NUM_SUBDIRS);
144
145 for (Searchpath sp : _valid_searchpaths) {
146 std::string buf = FioGetDirectory(sp, subdir);
147 buf += filename;
148 if (FileExists(buf)) return buf;
149 #if !defined(_WIN32)
150 /* Be, as opening files, aware that sometimes the filename
151 * might be in uppercase when it is in lowercase on the
152 * disk. Of course Windows doesn't care about casing. */
153 if (strtolower(buf, _searchpaths[sp].size() - 1) && FileExists(buf)) return buf;
154 #endif
155 }
156
157 return {};
158 }
159
160 std::string FioGetDirectory(Searchpath sp, Subdirectory subdir)
161 {
162 assert(subdir < NUM_SUBDIRS);
163 assert(sp < NUM_SEARCHPATHS);
164
165 return _searchpaths[sp] + _subdirs[subdir];
166 }
167
168 std::string FioFindDirectory(Subdirectory subdir)
169 {
170 /* Find and return the first valid directory */
171 for (Searchpath sp : _valid_searchpaths) {
172 std::string ret = FioGetDirectory(sp, subdir);
173 if (FileExists(ret)) return ret;
174 }
175
176 /* Could not find the directory, fall back to a base path */
177 return _personal_dir;
178 }
179
180 static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
181 {
182 #if defined(_WIN32)
183 /* fopen is implemented as a define with ellipses for
184 * Unicode support (prepend an L). As we are not sending
185 * a string, but a variable, it 'renames' the variable,
186 * so make that variable to makes it compile happily */
187 wchar_t Lmode[5];
188 MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
189 #endif
190 FILE *f = nullptr;
191 std::string buf;
192
193 if (subdir == NO_DIRECTORY) {
194 buf = filename;
195 } else {
196 buf = _searchpaths[sp] + _subdirs[subdir] + filename;
197 }
198
199 #if defined(_WIN32)
200 if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf).c_str()) == INVALID_FILE_ATTRIBUTES) return nullptr;
201 #endif
202
203 f = fopen(buf.c_str(), mode);
204 #if !defined(_WIN32)
205 if (f == nullptr && strtolower(buf, subdir == NO_DIRECTORY ? 0 : _searchpaths[sp].size() - 1) ) {
206 f = fopen(buf.c_str(), mode);
207 }
208 #endif
209 if (f != nullptr && filesize != nullptr) {
210 /* Find the size of the file */
211 fseek(f, 0, SEEK_END);
212 *filesize = ftell(f);
213 fseek(f, 0, SEEK_SET);
214 }
215 return f;
216 }
217
225 FILE *FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
226 {
227 FILE *f = fopen(entry.tar_filename.c_str(), "rb");
228 if (f == nullptr) return f;
229
230 if (fseek(f, entry.position, SEEK_SET) < 0) {
231 fclose(f);
232 return nullptr;
233 }
234
235 if (filesize != nullptr) *filesize = entry.size;
236 return f;
237 }
238
245 FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
246 {
247 FILE *f = nullptr;
248
249 assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
250
251 for (Searchpath sp : _valid_searchpaths) {
252 f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
253 if (f != nullptr || subdir == NO_DIRECTORY) break;
254 }
255
256 /* We can only use .tar in case of data-dir, and read-mode */
257 if (f == nullptr && mode[0] == 'r' && subdir != NO_DIRECTORY) {
258 /* Filenames in tars are always forced to be lowercase */
259 std::string resolved_name = filename;
260 strtolower(resolved_name);
261
262 /* Resolve ".." */
263 std::istringstream ss(resolved_name);
264 std::vector<std::string> tokens;
265 std::string token;
266 while (std::getline(ss, token, PATHSEPCHAR)) {
267 if (token == "..") {
268 if (tokens.size() < 2) return nullptr;
269 tokens.pop_back();
270 } else if (token == ".") {
271 /* Do nothing. "." means current folder, but you can create tar files with "." in the path.
272 * This confuses our file resolver. So, act like this folder doesn't exist. */
273 } else {
274 tokens.push_back(token);
275 }
276 }
277
278 resolved_name.clear();
279 bool first = true;
280 for (const std::string &token : tokens) {
281 if (!first) {
282 resolved_name += PATHSEP;
283 }
284 resolved_name += token;
285 first = false;
286 }
287
288 /* Resolve ONE directory link */
289 for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
290 const std::string &src = link->first;
291 size_t len = src.length();
292 if (resolved_name.length() >= len && resolved_name[len - 1] == PATHSEPCHAR && src.compare(0, len, resolved_name, 0, len) == 0) {
293 /* Apply link */
294 resolved_name.replace(0, len, link->second);
295 break; // Only resolve one level
296 }
297 }
298
299 TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
300 if (it != _tar_filelist[subdir].end()) {
301 f = FioFOpenFileTar(it->second, filesize);
302 }
303 }
304
305 /* Sometimes a full path is given. To support
306 * the 'subdirectory' must be 'removed'. */
307 if (f == nullptr && subdir != NO_DIRECTORY) {
308 switch (subdir) {
309 case BASESET_DIR:
310 f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
311 if (f != nullptr) break;
312 FALLTHROUGH;
313 case NEWGRF_DIR:
314 f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
315 break;
316
317 default:
318 f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
319 break;
320 }
321 }
322
323 return f;
324 }
325
331 void FioCreateDirectory(const std::string &name)
332 {
333 auto p = name.find_last_of(PATHSEPCHAR);
334 if (p != std::string::npos) {
335 std::string dirname = name.substr(0, p);
336 DIR *dir = ttd_opendir(dirname.c_str());
337 if (dir == nullptr) {
338 FioCreateDirectory(dirname); // Try creating the parent directory, if we couldn't open it
339 } else {
340 closedir(dir);
341 }
342 }
343
344 /* Ignore directory creation errors; they'll surface later on, and most
345 * of the time they are 'directory already exists' errors anyhow. */
346 #if defined(_WIN32)
347 CreateDirectory(OTTD2FS(name).c_str(), nullptr);
348 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
349 mkdir(OTTD2FS(name).c_str());
350 #else
351 mkdir(OTTD2FS(name).c_str(), 0755);
352 #endif
353 }
354
361 void AppendPathSeparator(std::string &buf)
362 {
363 if (buf.empty()) return;
364
365 if (buf.back() != PATHSEPCHAR) buf.push_back(PATHSEPCHAR);
366 }
367
368 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
369 {
370 std::string src = srcParam;
371 std::string dest = destParam;
372 /* Tar internals assume lowercase */
373 std::transform(src.begin(), src.end(), src.begin(), tolower);
374 std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
375
376 TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
377 if (dest_file != _tar_filelist[subdir].end()) {
378 /* Link to file. Process the link like the destination file. */
379 _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
380 } else {
381 /* Destination file not found. Assume 'link to directory'
382 * Append PATHSEPCHAR to 'src' and 'dest' if needed */
383 const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
384 const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
385 _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
386 }
387 }
388
394 static void SimplifyFileName(char *name)
395 {
396 /* Force lowercase */
397 strtolower(name);
398
399 /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
400 #if (PATHSEPCHAR != '/')
401 for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
402 #endif
403 }
404
410 uint TarScanner::DoScan(Subdirectory sd)
411 {
412 _tar_filelist[sd].clear();
413 _tar_list[sd].clear();
414 uint num = this->Scan(".tar", sd, false);
415 if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
416 return num;
417 }
418
419 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
420 {
421 Debug(misc, 1, "Scanning for tars");
422 TarScanner fs;
423 uint num = 0;
424 if (mode & TarScanner::BASESET) {
425 num += fs.DoScan(BASESET_DIR);
426 }
427 if (mode & TarScanner::NEWGRF) {
428 num += fs.DoScan(NEWGRF_DIR);
429 }
430 if (mode & TarScanner::AI) {
431 num += fs.DoScan(AI_DIR);
432 num += fs.DoScan(AI_LIBRARY_DIR);
433 }
434 if (mode & TarScanner::GAME) {
435 num += fs.DoScan(GAME_DIR);
436 num += fs.DoScan(GAME_LIBRARY_DIR);
437 }
438 if (mode & TarScanner::SCENARIO) {
439 num += fs.DoScan(SCENARIO_DIR);
440 num += fs.DoScan(HEIGHTMAP_DIR);
441 }
442 Debug(misc, 1, "Scan complete, found {} files", num);
443 return num;
444 }
445
452 bool TarScanner::AddFile(Subdirectory sd, const std::string &filename)
453 {
454 this->subdir = sd;
455 return this->AddFile(filename, 0);
456 }
457
458 bool TarScanner::AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename)
459 {
460 /* No tar within tar. */
461 assert(tar_filename.empty());
462
463 /* The TAR-header, repeated for every file */
464 struct TarHeader {
465 char name[100];
466 char mode[8];
467 char uid[8];
468 char gid[8];
469 char size[12];
470 char mtime[12];
471 char chksum[8];
472 char typeflag;
473 char linkname[100];
474 char magic[6];
475 char version[2];
476 char uname[32];
477 char gname[32];
478 char devmajor[8];
479 char devminor[8];
480 char prefix[155];
481
482 char unused[12];
483 };
484
485 /* Check if we already seen this file */
486 TarList::iterator it = _tar_list[this->subdir].find(filename);
487 if (it != _tar_list[this->subdir].end()) return false;
488
489 FILE *f = fopen(filename.c_str(), "rb");
490 /* Although the file has been found there can be
491 * a number of reasons we cannot open the file.
492 * Most common case is when we simply have not
493 * been given read access. */
494 if (f == nullptr) return false;
495
496 _tar_list[this->subdir][filename] = std::string{};
497
498 TarLinkList links;
499
500 TarHeader th;
501 char buf[sizeof(th.name) + 1], *end;
502 char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
503 char link[sizeof(th.linkname) + 1];
504 char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
505 size_t num = 0, pos = 0;
506
507 /* Make a char of 512 empty bytes */
508 char empty[512];
509 memset(&empty[0], 0, sizeof(empty));
510
511 for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
512 size_t num_bytes_read = fread(&th, 1, 512, f);
513 if (num_bytes_read != 512) break;
514 pos += num_bytes_read;
515
516 /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
517 if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
518 /* If we have only zeros in the block, it can be an end-of-file indicator */
519 if (memcmp(&th, &empty[0], 512) == 0) continue;
520
521 Debug(misc, 0, "The file '{}' isn't a valid tar-file", filename);
522 fclose(f);
523 return false;
524 }
525
526 name[0] = '\0';
527
528 /* The prefix contains the directory-name */
529 if (th.prefix[0] != '\0') {
530 strecpy(name, th.prefix, lastof(name));
531 strecat(name, PATHSEP, lastof(name));
532 }
533
534 /* Copy the name of the file in a safe way at the end of 'name' */
535 strecat(name, th.name, lastof(name));
536
537 /* Calculate the size of the file.. for some strange reason this is stored as a string */
538 strecpy(buf, th.size, lastof(buf));
539 size_t skip = strtoul(buf, &end, 8);
540
541 switch (th.typeflag) {
542 case '\0':
543 case '0': { // regular file
544 if (strlen(name) == 0) break;
545
546 /* Store this entry in the list */
547 TarFileListEntry entry;
548 entry.tar_filename = filename;
549 entry.size = skip;
550 entry.position = pos;
551
552 /* Convert to lowercase and our PATHSEPCHAR */
553 SimplifyFileName(name);
554
555 Debug(misc, 6, "Found file in tar: {} ({} bytes, {} offset)", name, skip, pos);
556 if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
557
558 break;
559 }
560
561 case '1': // hard links
562 case '2': { // symbolic links
563 /* Copy the destination of the link in a safe way at the end of 'linkname' */
564 strecpy(link, th.linkname, lastof(link));
565
566 if (strlen(name) == 0 || strlen(link) == 0) break;
567
568 /* Convert to lowercase and our PATHSEPCHAR */
569 SimplifyFileName(name);
570 SimplifyFileName(link);
571
572 /* Only allow relative links */
573 if (link[0] == PATHSEPCHAR) {
574 Debug(misc, 1, "Ignoring absolute link in tar: {} -> {}", name, link);
575 break;
576 }
577
578 /* Process relative path.
579 * Note: The destination of links must not contain any directory-links. */
580 strecpy(dest, name, lastof(dest));
581 char *destpos = strrchr(dest, PATHSEPCHAR);
582 if (destpos == nullptr) destpos = dest;
583 *destpos = '\0';
584
585 char *pos = link;
586 while (*pos != '\0') {
587 char *next = strchr(pos, PATHSEPCHAR);
588 if (next == nullptr) {
589 next = pos + strlen(pos);
590 } else {
591 /* Terminate the substring up to the path separator character. */
592 *next++= '\0';
593 }
594
595 if (strcmp(pos, ".") == 0) {
596 /* Skip '.' (current dir) */
597 } else if (strcmp(pos, "..") == 0) {
598 /* level up */
599 if (dest[0] == '\0') {
600 Debug(misc, 1, "Ignoring link pointing outside of data directory: {} -> {}", name, link);
601 break;
602 }
603
604 /* Truncate 'dest' after last PATHSEPCHAR.
605 * This assumes that the truncated part is a real directory and not a link. */
606 destpos = strrchr(dest, PATHSEPCHAR);
607 if (destpos == nullptr) destpos = dest;
608 *destpos = '\0';
609 } else {
610 /* Append at end of 'dest' */
611 if (destpos != dest) destpos = strecpy(destpos, PATHSEP, lastof(dest));
612 destpos = strecpy(destpos, pos, lastof(dest));
613 }
614
615 if (destpos >= lastof(dest)) {
616 Debug(misc, 0, "The length of a link in tar-file '{}' is too large (malformed?)", filename);
617 fclose(f);
618 return false;
619 }
620
621 pos = next;
622 }
623
624 /* Store links in temporary list */
625 Debug(misc, 6, "Found link in tar: {} -> {}", name, dest);
626 links.insert(TarLinkList::value_type(name, dest));
627
628 break;
629 }
630
631 case '5': // directory
632 /* Convert to lowercase and our PATHSEPCHAR */
633 SimplifyFileName(name);
634
635 /* Store the first directory name we detect */
636 Debug(misc, 6, "Found dir in tar: {}", name);
637 if (_tar_list[this->subdir][filename].empty()) _tar_list[this->subdir][filename] = name;
638 break;
639
640 default:
641 /* Ignore other types */
642 break;
643 }
644
645 /* Skip to the next block.. */
646 skip = Align(skip, 512);
647 if (fseek(f, skip, SEEK_CUR) < 0) {
648 Debug(misc, 0, "The file '{}' can't be read as a valid tar-file", filename);
649 fclose(f);
650 return false;
651 }
652 pos += skip;
653 }
654
655 Debug(misc, 1, "Found tar '{}' with {} new files", filename, num);
656 fclose(f);
657
658 /* Resolve file links and store directory links.
659 * We restrict usage of links to two cases:
660 * 1) Links to directories:
661 * Both the source path and the destination path must NOT contain any further links.
662 * When resolving files at most one directory link is resolved.
663 * 2) Links to files:
664 * The destination path must NOT contain any links.
665 * The source path may contain one directory link.
666 */
667 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
668 const std::string &src = link->first;
669 const std::string &dest = link->second;
670 TarAddLink(src, dest, this->subdir);
671 }
672
673 return true;
674 }
675
683 bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
684 {
685 TarList::iterator it = _tar_list[subdir].find(tar_filename);
686 /* We don't know the file. */
687 if (it == _tar_list[subdir].end()) return false;
688
689 const auto &dirname = (*it).second;
690
691 /* The file doesn't have a sub directory! */
692 if (dirname.empty()) {
693 Debug(misc, 1, "Extracting {} failed; archive rejected, the contents must be in a sub directory", tar_filename);
694 return false;
695 }
696
697 std::string filename = tar_filename;
698 auto p = filename.find_last_of(PATHSEPCHAR);
699 /* The file's path does not have a separator? */
700 if (p == std::string::npos) return false;
701
702 filename.replace(p + 1, std::string::npos, dirname);
703 Debug(misc, 8, "Extracting {} to directory {}", tar_filename, filename);
704 FioCreateDirectory(filename);
705
706 for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
707 if (tar_filename != it2->second.tar_filename) continue;
708
709 filename.replace(p + 1, std::string::npos, it2->first);
710
711 Debug(misc, 9, " extracting {}", filename);
712
713 /* First open the file in the .tar. */
714 size_t to_copy = 0;
715 std::unique_ptr<FILE, FileDeleter> in(FioFOpenFileTar(it2->second, &to_copy));
716 if (!in) {
717 Debug(misc, 6, "Extracting {} failed; could not open {}", filename, tar_filename);
718 return false;
719 }
720
721 /* Now open the 'output' file. */
722 std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(), "wb"));
723 if (!out) {
724 Debug(misc, 6, "Extracting {} failed; could not open {}", filename, filename);
725 return false;
726 }
727
728 /* Now read from the tar and write it into the file. */
729 char buffer[4096];
730 size_t read;
731 for (; to_copy != 0; to_copy -= read) {
732 read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), in.get());
733 if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read) break;
734 }
735
736 if (to_copy != 0) {
737 Debug(misc, 6, "Extracting {} failed; still {} bytes to copy", filename, to_copy);
738 return false;
739 }
740 }
741
742 Debug(misc, 9, " extraction successful");
743 return true;
744 }
745
746 #if defined(_WIN32)
747
752 extern void DetermineBasePaths(const char *exe);
753 #else /* defined(_WIN32) */
754
762 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
763 {
764 char tmp[MAX_PATH];
765 strecpy(tmp, exe, lastof(tmp));
766
767 bool success = false;
768 #ifdef WITH_COCOA
769 char *app_bundle = strchr(tmp, '.');
770 while (app_bundle != nullptr && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
771
772 if (app_bundle != nullptr) *app_bundle = '\0';
773 #endif /* WITH_COCOA */
774 char *s = strrchr(tmp, PATHSEPCHAR);
775 if (s != nullptr) {
776 *s = '\0';
777 if (chdir(tmp) != 0) {
778 Debug(misc, 0, "Directory with the binary does not exist?");
779 } else {
780 success = true;
781 }
782 }
783 return success;
784 }
785
796 bool DoScanWorkingDirectory()
797 {
798 /* No working directory, so nothing to do. */
799 if (_searchpaths[SP_WORKING_DIR].empty()) return false;
800
801 /* Working directory is root, so do nothing. */
802 if (_searchpaths[SP_WORKING_DIR] == PATHSEP) return false;
803
804 /* No personal/home directory, so the working directory won't be that. */
805 if (_searchpaths[SP_PERSONAL_DIR].empty()) return true;
806
807 std::string tmp = _searchpaths[SP_WORKING_DIR] + PERSONAL_DIR;
808 AppendPathSeparator(tmp);
809
810 return _searchpaths[SP_PERSONAL_DIR] != tmp;
811 }
812
818 static std::string GetHomeDir()
819 {
820 #ifdef __HAIKU__
821 BPath path;
822 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
823 return std::string(path.Path());
824 #else
825 const char *home_env = std::getenv("HOME"); // Stack var, shouldn't be freed
826 if (home_env != nullptr) return std::string(home_env);
827
828 const struct passwd *pw = getpwuid(getuid());
829 if (pw != nullptr) return std::string(pw->pw_dir);
830 #endif
831 return {};
832 }
833
838 void DetermineBasePaths(const char *exe)
839 {
840 std::string tmp;
841 const std::string homedir = GetHomeDir();
842 #ifdef USE_XDG
843 const char *xdg_data_home = std::getenv("XDG_DATA_HOME");
844 if (xdg_data_home != nullptr) {
845 tmp = xdg_data_home;
846 tmp += PATHSEP;
847 tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
848 AppendPathSeparator(tmp);
849 _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
850
851 tmp += "content_download";
852 AppendPathSeparator(tmp);
853 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR_XDG] = tmp;
854 } else if (!homedir.empty()) {
855 tmp = homedir;
856 tmp += PATHSEP ".local" PATHSEP "share" PATHSEP;
857 tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
858 AppendPathSeparator(tmp);
859 _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
860
861 tmp += "content_download";
862 AppendPathSeparator(tmp);
863 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR_XDG] = tmp;
864 } else {
865 _searchpaths[SP_PERSONAL_DIR_XDG].clear();
866 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR_XDG].clear();
867 }
868 #endif
869
870 #if defined(OS2) || !defined(WITH_PERSONAL_DIR)
871 _searchpaths[SP_PERSONAL_DIR].clear();
872 #else
873 if (!homedir.empty()) {
874 tmp = homedir;
875 tmp += PATHSEP;
876 tmp += PERSONAL_DIR;
877 AppendPathSeparator(tmp);
878 _searchpaths[SP_PERSONAL_DIR] = tmp;
879
880 tmp += "content_download";
881 AppendPathSeparator(tmp);
882 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR] = tmp;
883 } else {
884 _searchpaths[SP_PERSONAL_DIR].clear();
885 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR].clear();
886 }
887 #endif
888
889 #if defined(WITH_SHARED_DIR)
890 tmp = SHARED_DIR;
891 AppendPathSeparator(tmp);
892 _searchpaths[SP_SHARED_DIR] = tmp;
893 #else
894 _searchpaths[SP_SHARED_DIR].clear();
895 #endif
896
897 char cwd[MAX_PATH];
898 if (getcwd(cwd, MAX_PATH) == nullptr) *cwd = '\0';
899
900 if (_config_file.empty()) {
901 /* Get the path to working directory of OpenTTD. */
902 tmp = cwd;
903 AppendPathSeparator(tmp);
904 _searchpaths[SP_WORKING_DIR] = tmp;
905
906 _do_scan_working_directory = DoScanWorkingDirectory();
907 } else {
908 /* Use the folder of the config file as working directory. */
909 size_t end = _config_file.find_last_of(PATHSEPCHAR);
910 if (end == std::string::npos) {
911 /* _config_file is not in a folder, so use current directory. */
912 tmp = cwd;
913 AppendPathSeparator(tmp);
914 _searchpaths[SP_WORKING_DIR] = tmp;
915 } else {
916 _searchpaths[SP_WORKING_DIR] = _config_file.substr(0, end + 1);
917 }
918 }
919
920 /* Change the working directory to that one of the executable */
921 if (ChangeWorkingDirectoryToExecutable(exe)) {
922 char buf[MAX_PATH];
923 if (getcwd(buf, lengthof(buf)) == nullptr) {
924 tmp.clear();
925 } else {
926 tmp = buf;
927 }
928 AppendPathSeparator(tmp);
929 _searchpaths[SP_BINARY_DIR] = tmp;
930 } else {
931 _searchpaths[SP_BINARY_DIR].clear();
932 }
933
934 if (cwd[0] != '\0') {
935 /* Go back to the current working directory. */
936 if (chdir(cwd) != 0) {
937 Debug(misc, 0, "Failed to return to working directory!");
938 }
939 }
940
941 #if !defined(GLOBAL_DATA_DIR)
942 _searchpaths[SP_INSTALLATION_DIR].clear();
943 #else
944 tmp = GLOBAL_DATA_DIR;
945 AppendPathSeparator(tmp);
946 _searchpaths[SP_INSTALLATION_DIR] = tmp;
947 #endif
948 #ifdef WITH_COCOA
949 extern void CocoaSetApplicationBundleDir();
950 CocoaSetApplicationBundleDir();
951 #else
952 _searchpaths[SP_APPLICATION_BUNDLE_DIR].clear();
953 #endif
954 }
955 #endif /* defined(_WIN32) */
956
957 std::string _personal_dir;
958
966 void DeterminePaths(const char *exe, bool only_local_path)
967 {
968 DetermineBasePaths(exe);
969 FillValidSearchPaths(only_local_path);
970
971 #ifdef USE_XDG
972 std::string config_home;
973 const std::string homedir = GetHomeDir();
974 const char *xdg_config_home = std::getenv("XDG_CONFIG_HOME");
975 if (xdg_config_home != nullptr) {
976 config_home = xdg_config_home;
977 config_home += PATHSEP;
978 config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
979 } else if (!homedir.empty()) {
980 /* Defaults to ~/.config */
981 config_home = homedir;
982 config_home += PATHSEP ".config" PATHSEP;
983 config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
984 }
985 AppendPathSeparator(config_home);
986 #endif
987
988 for (Searchpath sp : _valid_searchpaths) {
989 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
990 Debug(misc, 4, "{} added as search path", _searchpaths[sp]);
991 }
992
993 std::string config_dir;
994 if (!_config_file.empty()) {
995 config_dir = _searchpaths[SP_WORKING_DIR];
996 } else {
997 std::string personal_dir = FioFindFullPath(BASE_DIR, "openttd.cfg");
998 if (!personal_dir.empty()) {
999 auto end = personal_dir.find_last_of(PATHSEPCHAR);
1000 if (end != std::string::npos) personal_dir.erase(end + 1);
1001 config_dir = personal_dir;
1002 } else {
1003 #ifdef USE_XDG
1004 /* No previous configuration file found. Use the configuration folder from XDG. */
1005 config_dir = config_home;
1006 #else
1007 static const Searchpath new_openttd_cfg_order[] = {
1008 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
1009 };
1010
1011 config_dir.clear();
1012 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1013 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1014 config_dir = _searchpaths[new_openttd_cfg_order[i]];
1015 break;
1016 }
1017 }
1018 #endif
1019 }
1020 _config_file = config_dir + "openttd.cfg";
1021 }
1022
1023 Debug(misc, 3, "{} found as config directory", config_dir);
1024
1025 _highscore_file = config_dir + "hs.dat";
1026 extern std::string _hotkeys_file;
1027 _hotkeys_file = config_dir + "hotkeys.cfg";
1028 extern std::string _windows_file;
1029 _windows_file = config_dir + "windows.cfg";
1030 extern std::string _private_file;
1031 _private_file = config_dir + "private.cfg";
1032 extern std::string _secrets_file;
1033 _secrets_file = config_dir + "secrets.cfg";
1034
1035 #ifdef USE_XDG
1036 if (config_dir == config_home) {
1037 /* We are using the XDG configuration home for the config file,
1038 * then store the rest in the XDG data home folder. */
1039 _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1040 if (only_local_path) {
1041 /* In case of XDG and we only want local paths and we detected that
1042 * the user either manually indicated the XDG path or didn't use
1043 * "-c" option, we change the working-dir to the XDG personal-dir,
1044 * as this is most likely what the user is expecting. */
1045 _searchpaths[SP_WORKING_DIR] = _searchpaths[SP_PERSONAL_DIR_XDG];
1046 }
1047 } else
1048 #endif
1049 {
1050 _personal_dir = config_dir;
1051 }
1052
1053 /* Make the necessary folders */
1054 FioCreateDirectory(config_dir);
1055 #if defined(WITH_PERSONAL_DIR)
1056 FioCreateDirectory(_personal_dir);
1057 #endif
1058
1059 Debug(misc, 3, "{} found as personal directory", _personal_dir);
1060
1061 static const Subdirectory default_subdirs[] = {
1062 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR
1063 };
1064
1065 for (uint i = 0; i < lengthof(default_subdirs); i++) {
1066 FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
1067 }
1068
1069 /* If we have network we make a directory for the autodownloading of content */
1070 _searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
1071 Debug(misc, 4, "{} added as search path", _searchpaths[SP_AUTODOWNLOAD_DIR]);
1072 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
1073 FillValidSearchPaths(only_local_path);
1074
1075 /* Create the directory for each of the types of content */
1076 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };
1077 for (uint i = 0; i < lengthof(dirs); i++) {
1078 FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
1079 }
1080
1081 extern std::string _log_file;
1082 _log_file = _personal_dir + "openttd.log";
1083 }
1084
1089 void SanitizeFilename(char *filename)
1090 {
1091 for (; *filename != '\0'; filename++) {
1092 switch (*filename) {
1093 /* The following characters are not allowed in filenames
1094 * on at least one of the supported operating systems: */
1095 case ':': case '\\': case '*': case '?': case '/':
1096 case '<': case '>': case '|': case '"':
1097 *filename = '_';
1098 break;
1099 }
1100 }
1101 }
1102
1111 std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
1112 {
1113 FILE *in = fopen(filename.c_str(), "rb");
1114 if (in == nullptr) return nullptr;
1115
1116 FileCloser fc(in);
1117
1118 fseek(in, 0, SEEK_END);
1119 size_t len = ftell(in);
1120 fseek(in, 0, SEEK_SET);
1121 if (len > maxsize) return nullptr;
1122
1123 std::unique_ptr<char[]> mem = std::make_unique<char[]>(len + 1);
1124
1125 mem.get()[len] = 0;
1126 if (fread(mem.get(), len, 1, in) != 1) return nullptr;
1127
1128 lenp = len;
1129 return mem;
1130 }
1131
1138 static bool MatchesExtension(const char *extension, const char *filename)
1139 {
1140 if (extension == nullptr) return true;
1141
1142 const char *ext = strrchr(filename, extension[0]);
1143 return ext != nullptr && strcasecmp(ext, extension) == 0;
1144 }
1145
1155 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1156 {
1157 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
1158
1159 uint num = 0;
1160 struct stat sb;
1161 struct dirent *dirent;
1162 DIR *dir;
1163
1164 if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1165
1166 while ((dirent = readdir(dir)) != nullptr) {
1167 std::string d_name = FS2OTTD(dirent->d_name);
1168
1169 if (!FiosIsValidFile(path, dirent, &sb)) continue;
1170
1171 std::string filename(path);
1172 filename += d_name;
1173
1174 if (S_ISDIR(sb.st_mode)) {
1175 /* Directory */
1176 if (!recursive) continue;
1177 if (d_name == "." || d_name == "..") continue;
1178 AppendPathSeparator(filename);
1179 num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
1180 } else if (S_ISREG(sb.st_mode)) {
1181 /* File */
1182 if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, basepath_length, {})) num++;
1183 }
1184 }
1185
1186 closedir(dir);
1187
1188 return num;
1189 }
1190
1197 static uint ScanTar(FileScanner *fs, const char *extension, const TarFileList::value_type &tar)
1198 {
1199 uint num = 0;
1200 const auto &filename = tar.first;
1201
1202 if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, 0, tar.second.tar_filename)) num++;
1203
1204 return num;
1205 }
1206
1216 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1217 {
1218 this->subdir = sd;
1219
1220 uint num = 0;
1221
1222 for (Searchpath sp : _valid_searchpaths) {
1223 /* Don't search in the working directory */
1224 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1225
1226 std::string path = FioGetDirectory(sp, sd);
1227 num += ScanPath(this, extension, path.c_str(), path.size(), recursive);
1228 }
1229
1230 if (tars && sd != NO_DIRECTORY) {
1231 for (const auto &tar : _tar_filelist[sd]) {
1232 num += ScanTar(this, extension, tar);
1233 }
1234 }
1235
1236 switch (sd) {
1237 case BASESET_DIR:
1238 num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1239 FALLTHROUGH;
1240 case NEWGRF_DIR:
1241 num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1242 break;
1243
1244 default: break;
1245 }
1246
1247 return num;
1248 }
1249
1258 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
1259 {
1260 std::string path(directory);
1261 AppendPathSeparator(path);
1262 return ScanPath(this, extension, path.c_str(), path.size(), recursive);
1263 }
ScanTar
static uint ScanTar(FileScanner *fs, const char *extension, const TarFileList::value_type &tar)
Scan the given tar and add graphics sets when it finds one.
Definition: fileio.cpp:1197
MatchesExtension
static bool MatchesExtension(const char *extension, const char *filename)
Helper to see whether a given filename matches the extension.
Definition: fileio.cpp:1138
SP_AUTODOWNLOAD_DIR
@ SP_AUTODOWNLOAD_DIR
Search within the autodownload directory.
Definition: fileio_type.h:142
DeterminePaths
void DeterminePaths(const char *exe, bool only_local_path)
Acquire the base paths (personal dir and game data dir), fill all other paths (save dir,...
Definition: fileio.cpp:966
ExtractTar
bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
Extract the tar with the given filename in the directory where the tar resides.
Definition: fileio.cpp:683
SAVE_DIR
@ SAVE_DIR
Base directory for all savegames.
Definition: fileio_type.h:110
_tar_linklist
static TarLinkList _tar_linklist[NUM_SUBDIRS]
List of directory links.
Definition: fileio.cpp:70
_personal_dir
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:957
strtolower
bool strtolower(char *str)
Convert a given ASCII string to lowercase.
Definition: string.cpp:447
SP_PERSONAL_DIR
@ SP_PERSONAL_DIR
Search in the personal directory.
Definition: fileio_type.h:137
BASESET_DIR
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:116
TarScanner::DoScan
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:410
GAME_LIBRARY_DIR
@ GAME_LIBRARY_DIR
Subdirectory for all GS libraries.
Definition: fileio_type.h:122
SCREENSHOT_DIR
@ SCREENSHOT_DIR
Subdirectory for all screenshots.
Definition: fileio_type.h:123
SP_AUTODOWNLOAD_PERSONAL_DIR
@ SP_AUTODOWNLOAD_PERSONAL_DIR
Search within the autodownload directory located in the personal directory.
Definition: fileio_type.h:143
Searchpath
Searchpath
Types of searchpaths OpenTTD might use.
Definition: fileio_type.h:131
NUM_SUBDIRS
@ NUM_SUBDIRS
Number of subdirectories.
Definition: fileio_type.h:124
FileScanner::Scan
uint Scan(const char *extension, Subdirectory sd, bool tars=true, bool recursive=true)
Scan for files with the given extension in the given search path.
Definition: fileio.cpp:1216
HEIGHTMAP_DIR
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
Definition: fileio_type.h:113
spriteloader.hpp
SP_AUTODOWNLOAD_PERSONAL_DIR_XDG
@ SP_AUTODOWNLOAD_PERSONAL_DIR_XDG
Search within the autodownload directory located in the personal directory (XDG variant)
Definition: fileio_type.h:144
fileio_func.h
SP_INSTALLATION_DIR
@ SP_INSTALLATION_DIR
Search in the installation directory.
Definition: fileio_type.h:140
AUTOSAVE_DIR
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
Definition: fileio_type.h:111
_private_file
std::string _private_file
Private configuration file of OpenTTD.
Definition: settings.cpp:57
fios.h
FioFOpenFileTar
FILE * FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
Opens a file from inside a tar archive.
Definition: fileio.cpp:225
OLD_GM_DIR
@ OLD_GM_DIR
Old subdirectory for the music.
Definition: fileio_type.h:114
FileCloser
Auto-close a file upon scope exit.
Definition: fileio_func.h:120
BASE_DIR
@ BASE_DIR
Base directory for all subdirectories.
Definition: fileio_type.h:109
Align
static T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Definition: math_func.hpp:35
_searchpaths
std::array< std::string, NUM_SEARCHPATHS > _searchpaths
The search paths OpenTTD could search through.
Definition: fileio.cpp:56
FS2OTTD
std::string FS2OTTD(const std::wstring &name)
Convert to OpenTTD's encoding from a wide string.
Definition: win32.cpp:542
AI_DIR
@ AI_DIR
Subdirectory for all AI files.
Definition: fileio_type.h:119
FioFOpenFile
FILE * FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition: fileio.cpp:245
_do_scan_working_directory
static bool _do_scan_working_directory
Whether the working directory should be scanned.
Definition: fileio.cpp:34
_log_file
std::string _log_file
File to reroute output of a forked OpenTTD to.
Definition: dedicated.cpp:14
tar_type.h
FileExists
bool FileExists(const std::string &filename)
Test whether the given filename exists.
Definition: fileio.cpp:122
ChangeWorkingDirectoryToExecutable
static bool ChangeWorkingDirectoryToExecutable(const char *exe)
Changes the working directory to the path of the give executable.
Definition: fileio.cpp:762
FileScanner::AddFile
virtual bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename)=0
Add a file with the given filename.
SP_APPLICATION_BUNDLE_DIR
@ SP_APPLICATION_BUNDLE_DIR
Search within the application bundle.
Definition: fileio_type.h:141
ReadFileToMem
std::unique_ptr< char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
Load a file into memory.
Definition: fileio.cpp:1111
safeguards.h
GAME_DIR
@ GAME_DIR
Subdirectory for all game scripts.
Definition: fileio_type.h:121
SCENARIO_DIR
@ SCENARIO_DIR
Base directory for all scenarios.
Definition: fileio_type.h:112
FileScanner::subdir
Subdirectory subdir
The current sub directory we are searching through.
Definition: fileio_func.h:41
TarScanner::NEWGRF
@ NEWGRF
Scan for non-base sets.
Definition: fileio_func.h:68
FioCreateDirectory
void FioCreateDirectory(const std::string &name)
Create a directory with the given name If the parent directory does not exist, it will try to create ...
Definition: fileio.cpp:331
SP_SHARED_DIR
@ SP_SHARED_DIR
Search in the shared directory, like 'Shared Files' under Windows.
Definition: fileio_type.h:138
stdafx.h
SP_WORKING_DIR
@ SP_WORKING_DIR
Search in the working directory.
Definition: fileio_type.h:133
NEWGRF_DIR
@ NEWGRF_DIR
Subdirectory for all NewGRFs.
Definition: fileio_type.h:117
AppendPathSeparator
void AppendPathSeparator(std::string &buf)
Appends, if necessary, the path separator character to the end of the string.
Definition: fileio.cpp:361
TarScanner::GAME
@ GAME
Scan for game scripts.
Definition: fileio_func.h:71
TarScanner::AddFile
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename={}) override
Add a file with the given filename.
Definition: fileio.cpp:458
_secrets_file
std::string _secrets_file
Secrets configuration file of OpenTTD.
Definition: settings.cpp:58
string_func.h
_windows_file
std::string _windows_file
Config file to store WindowDesc.
Definition: window.cpp:102
SP_BINARY_DIR
@ SP_BINARY_DIR
Search in the directory where the binary resides.
Definition: fileio_type.h:139
TarScanner::SCENARIO
@ SCENARIO
Scan for scenarios and heightmaps.
Definition: fileio_func.h:70
_highscore_file
std::string _highscore_file
The file to store the highscore data in.
Definition: highscore.cpp:23
FioCheckFileExists
bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
Check whether the given file exists.
Definition: fileio.cpp:108
NO_DIRECTORY
@ NO_DIRECTORY
A path without any base directory.
Definition: fileio_type.h:125
TarScanner::AI
@ AI
Scan for AIs and its libraries.
Definition: fileio_func.h:69
GetHomeDir
static std::string GetHomeDir()
Gets the home directory of the user.
Definition: fileio.cpp:818
DIR
Definition: win32.cpp:68
SanitizeFilename
void SanitizeFilename(char *filename)
Sanitizes a filename, i.e.
Definition: fileio.cpp:1089
ScanPath
static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
Scan a single directory (and recursively its children) and add any graphics sets that are found.
Definition: fileio.cpp:1155
SimplifyFileName
static void SimplifyFileName(char *name)
Simplify filenames from tars.
Definition: fileio.cpp:394
Subdirectory
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition: fileio_type.h:108
DetermineBasePaths
void DetermineBasePaths(const char *exe)
Determine the base (personal dir and game data dir) paths.
Definition: fileio.cpp:838
AI_LIBRARY_DIR
@ AI_LIBRARY_DIR
Subdirectory for all AI libraries.
Definition: fileio_type.h:120
Debug
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
TarScanner
Helper for scanning for files with tar as extension.
Definition: fileio_func.h:61
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:378
FileScanner
Helper for scanning for files with a given name.
Definition: fileio_func.h:39
TarScanner::BASESET
@ BASESET
Scan for base sets.
Definition: fileio_func.h:67
IsValidSearchPath
static bool IsValidSearchPath(Searchpath sp)
Checks whether the given search path is a valid search path.
Definition: fileio.cpp:77
OLD_DATA_DIR
@ OLD_DATA_DIR
Old subdirectory for the data.
Definition: fileio_type.h:115
FioFindFullPath
std::string FioFindFullPath(Subdirectory subdir, const char *filename)
Find a path to the filename in one of the search directories.
Definition: fileio.cpp:141
strecpy
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: string.cpp:112
ttd_opendir
static DIR * ttd_opendir(const char *path)
A wrapper around opendir() which will convert the string from OPENTTD encoding to that of the filesys...
Definition: fileio_func.h:113
strecat
char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
Definition: string.cpp:84
TarScanner::Mode
Mode
The mode of tar scanning.
Definition: fileio_func.h:65
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:394
OTTD2FS
std::wstring OTTD2FS(const std::string &name)
Convert from OpenTTD's encoding to a wide string.
Definition: win32.cpp:560
DoScanWorkingDirectory
bool DoScanWorkingDirectory()
Whether we should scan the working directory.
Definition: fileio.cpp:796
FioFCloseFile
void FioFCloseFile(FILE *f)
Close a file in a safe way.
Definition: fileio.cpp:130
debug.h
_config_file
std::string _config_file
Configuration file of OpenTTD.
Definition: settings.cpp:56
TarFileListEntry
Definition: tar_type.h:20