OpenTTD Source: src/fileio.cpp Source File

OpenTTD Source 14.0-RC3

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