OpenTTD Source: src/fileio.cpp Source File

OpenTTD Source 14.0-beta1

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 filename.replace(p + 1, std::string::npos, it2.first);
704
705 Debug(misc, 9, " extracting {}", filename);
706
707 /* First open the file in the .tar. */
708 size_t to_copy = 0;
709 std::unique_ptr<FILE, FileDeleter> in(FioFOpenFileTar(it2.second, &to_copy));
710 if (!in) {
711 Debug(misc, 6, "Extracting {} failed; could not open {}", filename, tar_filename);
712 return false;
713 }
714
715 /* Now open the 'output' file. */
716 std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(), "wb"));
717 if (!out) {
718 Debug(misc, 6, "Extracting {} failed; could not open {}", filename, filename);
719 return false;
720 }
721
722 /* Now read from the tar and write it into the file. */
723 char buffer[4096];
724 size_t read;
725 for (; to_copy != 0; to_copy -= read) {
726 read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), in.get());
727 if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read) break;
728 }
729
730 if (to_copy != 0) {
731 Debug(misc, 6, "Extracting {} failed; still {} bytes to copy", filename, to_copy);
732 return false;
733 }
734 }
735
736 Debug(misc, 9, " extraction successful");
737 return true;
738 }
739
740 #if defined(_WIN32)
741
746 extern void DetermineBasePaths(const char *exe);
747 #else /* defined(_WIN32) */
748
756 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
757 {
758 std::string path = exe;
759
760 #ifdef WITH_COCOA
761 for (size_t pos = path.find_first_of('.'); pos != std::string::npos; pos = path.find_first_of('.', pos + 1)) {
762 if (StrEqualsIgnoreCase(path.substr(pos, 4), ".app")) {
763 path.erase(pos);
764 break;
765 }
766 }
767 #endif /* WITH_COCOA */
768
769 size_t pos = path.find_last_of(PATHSEPCHAR);
770 if (pos == std::string::npos) return false;
771
772 path.erase(pos);
773
774 if (chdir(path.c_str()) != 0) {
775 Debug(misc, 0, "Directory with the binary does not exist?");
776 return false;
777 }
778
779 return true;
780 }
781
792 bool DoScanWorkingDirectory()
793 {
794 /* No working directory, so nothing to do. */
795 if (_searchpaths[SP_WORKING_DIR].empty()) return false;
796
797 /* Working directory is root, so do nothing. */
798 if (_searchpaths[SP_WORKING_DIR] == PATHSEP) return false;
799
800 /* No personal/home directory, so the working directory won't be that. */
801 if (_searchpaths[SP_PERSONAL_DIR].empty()) return true;
802
803 std::string tmp = _searchpaths[SP_WORKING_DIR] + PERSONAL_DIR;
804 AppendPathSeparator(tmp);
805
806 return _searchpaths[SP_PERSONAL_DIR] != tmp;
807 }
808
814 static std::string GetHomeDir()
815 {
816 #ifdef __HAIKU__
817 BPath path;
818 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
819 return std::string(path.Path());
820 #else
821 const char *home_env = std::getenv("HOME"); // Stack var, shouldn't be freed
822 if (home_env != nullptr) return std::string(home_env);
823
824 const struct passwd *pw = getpwuid(getuid());
825 if (pw != nullptr) return std::string(pw->pw_dir);
826 #endif
827 return {};
828 }
829
834 void DetermineBasePaths(const char *exe)
835 {
836 std::string tmp;
837 const std::string homedir = GetHomeDir();
838 #ifdef USE_XDG
839 const char *xdg_data_home = std::getenv("XDG_DATA_HOME");
840 if (xdg_data_home != nullptr) {
841 tmp = xdg_data_home;
842 tmp += PATHSEP;
843 tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
844 AppendPathSeparator(tmp);
845 _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
846
847 tmp += "content_download";
848 AppendPathSeparator(tmp);
849 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR_XDG] = tmp;
850 } else if (!homedir.empty()) {
851 tmp = homedir;
852 tmp += PATHSEP ".local" PATHSEP "share" PATHSEP;
853 tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
854 AppendPathSeparator(tmp);
855 _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
856
857 tmp += "content_download";
858 AppendPathSeparator(tmp);
859 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR_XDG] = tmp;
860 } else {
861 _searchpaths[SP_PERSONAL_DIR_XDG].clear();
862 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR_XDG].clear();
863 }
864 #endif
865
866 #if !defined(WITH_PERSONAL_DIR)
867 _searchpaths[SP_PERSONAL_DIR].clear();
868 #else
869 if (!homedir.empty()) {
870 tmp = homedir;
871 tmp += PATHSEP;
872 tmp += PERSONAL_DIR;
873 AppendPathSeparator(tmp);
874 _searchpaths[SP_PERSONAL_DIR] = tmp;
875
876 tmp += "content_download";
877 AppendPathSeparator(tmp);
878 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR] = tmp;
879 } else {
880 _searchpaths[SP_PERSONAL_DIR].clear();
881 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR].clear();
882 }
883 #endif
884
885 #if defined(WITH_SHARED_DIR)
886 tmp = SHARED_DIR;
887 AppendPathSeparator(tmp);
888 _searchpaths[SP_SHARED_DIR] = tmp;
889 #else
890 _searchpaths[SP_SHARED_DIR].clear();
891 #endif
892
893 char cwd[MAX_PATH];
894 if (getcwd(cwd, MAX_PATH) == nullptr) *cwd = '\0';
895
896 if (_config_file.empty()) {
897 /* Get the path to working directory of OpenTTD. */
898 tmp = cwd;
899 AppendPathSeparator(tmp);
900 _searchpaths[SP_WORKING_DIR] = tmp;
901
902 _do_scan_working_directory = DoScanWorkingDirectory();
903 } else {
904 /* Use the folder of the config file as working directory. */
905 size_t end = _config_file.find_last_of(PATHSEPCHAR);
906 if (end == std::string::npos) {
907 /* _config_file is not in a folder, so use current directory. */
908 tmp = cwd;
909 AppendPathSeparator(tmp);
910 _searchpaths[SP_WORKING_DIR] = tmp;
911 } else {
912 _searchpaths[SP_WORKING_DIR] = _config_file.substr(0, end + 1);
913 }
914 }
915
916 /* Change the working directory to that one of the executable */
917 if (ChangeWorkingDirectoryToExecutable(exe)) {
918 char buf[MAX_PATH];
919 if (getcwd(buf, lengthof(buf)) == nullptr) {
920 tmp.clear();
921 } else {
922 tmp = buf;
923 }
924 AppendPathSeparator(tmp);
925 _searchpaths[SP_BINARY_DIR] = tmp;
926 } else {
927 _searchpaths[SP_BINARY_DIR].clear();
928 }
929
930 if (cwd[0] != '\0') {
931 /* Go back to the current working directory. */
932 if (chdir(cwd) != 0) {
933 Debug(misc, 0, "Failed to return to working directory!");
934 }
935 }
936
937 #if !defined(GLOBAL_DATA_DIR)
938 _searchpaths[SP_INSTALLATION_DIR].clear();
939 #else
940 tmp = GLOBAL_DATA_DIR;
941 AppendPathSeparator(tmp);
942 _searchpaths[SP_INSTALLATION_DIR] = tmp;
943 #endif
944 #ifdef WITH_COCOA
945 extern void CocoaSetApplicationBundleDir();
946 CocoaSetApplicationBundleDir();
947 #else
948 _searchpaths[SP_APPLICATION_BUNDLE_DIR].clear();
949 #endif
950 }
951 #endif /* defined(_WIN32) */
952
953 std::string _personal_dir;
954
962 void DeterminePaths(const char *exe, bool only_local_path)
963 {
964 DetermineBasePaths(exe);
965 FillValidSearchPaths(only_local_path);
966
967 #ifdef USE_XDG
968 std::string config_home;
969 const std::string homedir = GetHomeDir();
970 const char *xdg_config_home = std::getenv("XDG_CONFIG_HOME");
971 if (xdg_config_home != nullptr) {
972 config_home = xdg_config_home;
973 config_home += PATHSEP;
974 config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
975 } else if (!homedir.empty()) {
976 /* Defaults to ~/.config */
977 config_home = homedir;
978 config_home += PATHSEP ".config" PATHSEP;
979 config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
980 }
981 AppendPathSeparator(config_home);
982 #endif
983
984 for (Searchpath sp : _valid_searchpaths) {
985 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
986 Debug(misc, 3, "{} added as search path", _searchpaths[sp]);
987 }
988
989 std::string config_dir;
990 if (!_config_file.empty()) {
991 config_dir = _searchpaths[SP_WORKING_DIR];
992 } else {
993 std::string personal_dir = FioFindFullPath(BASE_DIR, "openttd.cfg");
994 if (!personal_dir.empty()) {
995 auto end = personal_dir.find_last_of(PATHSEPCHAR);
996 if (end != std::string::npos) personal_dir.erase(end + 1);
997 config_dir = personal_dir;
998 } else {
999 #ifdef USE_XDG
1000 /* No previous configuration file found. Use the configuration folder from XDG. */
1001 config_dir = config_home;
1002 #else
1003 static const Searchpath new_openttd_cfg_order[] = {
1004 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
1005 };
1006
1007 config_dir.clear();
1008 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1009 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1010 config_dir = _searchpaths[new_openttd_cfg_order[i]];
1011 break;
1012 }
1013 }
1014 #endif
1015 }
1016 _config_file = config_dir + "openttd.cfg";
1017 }
1018
1019 Debug(misc, 1, "{} found as config directory", config_dir);
1020
1021 _highscore_file = config_dir + "hs.dat";
1022 extern std::string _hotkeys_file;
1023 _hotkeys_file = config_dir + "hotkeys.cfg";
1024 extern std::string _windows_file;
1025 _windows_file = config_dir + "windows.cfg";
1026 extern std::string _private_file;
1027 _private_file = config_dir + "private.cfg";
1028 extern std::string _secrets_file;
1029 _secrets_file = config_dir + "secrets.cfg";
1030
1031 #ifdef USE_XDG
1032 if (config_dir == config_home) {
1033 /* We are using the XDG configuration home for the config file,
1034 * then store the rest in the XDG data home folder. */
1035 _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1036 if (only_local_path) {
1037 /* In case of XDG and we only want local paths and we detected that
1038 * the user either manually indicated the XDG path or didn't use
1039 * "-c" option, we change the working-dir to the XDG personal-dir,
1040 * as this is most likely what the user is expecting. */
1041 _searchpaths[SP_WORKING_DIR] = _searchpaths[SP_PERSONAL_DIR_XDG];
1042 }
1043 } else
1044 #endif
1045 {
1046 _personal_dir = config_dir;
1047 }
1048
1049 /* Make the necessary folders */
1050 FioCreateDirectory(config_dir);
1051 #if defined(WITH_PERSONAL_DIR)
1052 FioCreateDirectory(_personal_dir);
1053 #endif
1054
1055 Debug(misc, 1, "{} found as personal directory", _personal_dir);
1056
1057 static const Subdirectory default_subdirs[] = {
1058 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
1059 };
1060
1061 for (uint i = 0; i < lengthof(default_subdirs); i++) {
1062 FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
1063 }
1064
1065 /* If we have network we make a directory for the autodownloading of content */
1066 _searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
1067 Debug(misc, 3, "{} added as search path", _searchpaths[SP_AUTODOWNLOAD_DIR]);
1068 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
1069 FillValidSearchPaths(only_local_path);
1070
1071 /* Create the directory for each of the types of content */
1072 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SOCIAL_INTEGRATION_DIR };
1073 for (uint i = 0; i < lengthof(dirs); i++) {
1074 FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
1075 }
1076
1077 extern std::string _log_file;
1078 _log_file = _personal_dir + "openttd.log";
1079 }
1080
1085 void SanitizeFilename(std::string &filename)
1086 {
1087 for (auto &c : filename) {
1088 switch (c) {
1089 /* The following characters are not allowed in filenames
1090 * on at least one of the supported operating systems: */
1091 case ':': case '\\': case '*': case '?': case '/':
1092 case '<': case '>': case '|': case '"':
1093 c = '_';
1094 break;
1095 }
1096 }
1097 }
1098
1107 std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
1108 {
1109 FILE *in = fopen(filename.c_str(), "rb");
1110 if (in == nullptr) return nullptr;
1111
1112 FileCloser fc(in);
1113
1114 fseek(in, 0, SEEK_END);
1115 size_t len = ftell(in);
1116 fseek(in, 0, SEEK_SET);
1117 if (len > maxsize) return nullptr;
1118
1119 std::unique_ptr<char[]> mem = std::make_unique<char[]>(len + 1);
1120
1121 mem.get()[len] = 0;
1122 if (fread(mem.get(), len, 1, in) != 1) return nullptr;
1123
1124 lenp = len;
1125 return mem;
1126 }
1127
1134 static bool MatchesExtension(const char *extension, const char *filename)
1135 {
1136 if (extension == nullptr) return true;
1137
1138 const char *ext = strrchr(filename, extension[0]);
1139 return ext != nullptr && StrEqualsIgnoreCase(ext, extension);
1140 }
1141
1151 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1152 {
1153 uint num = 0;
1154 struct stat sb;
1155 struct dirent *dirent;
1156 DIR *dir;
1157
1158 if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1159
1160 while ((dirent = readdir(dir)) != nullptr) {
1161 std::string d_name = FS2OTTD(dirent->d_name);
1162
1163 if (!FiosIsValidFile(path, dirent, &sb)) continue;
1164
1165 std::string filename(path);
1166 filename += d_name;
1167
1168 if (S_ISDIR(sb.st_mode)) {
1169 /* Directory */
1170 if (!recursive) continue;
1171 if (d_name == "." || d_name == "..") continue;
1172 AppendPathSeparator(filename);
1173 num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
1174 } else if (S_ISREG(sb.st_mode)) {
1175 /* File */
1176 if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, basepath_length, {})) num++;
1177 }
1178 }
1179
1180 closedir(dir);
1181
1182 return num;
1183 }
1184
1191 static uint ScanTar(FileScanner *fs, const char *extension, const TarFileList::value_type &tar)
1192 {
1193 uint num = 0;
1194 const auto &filename = tar.first;
1195
1196 if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, 0, tar.second.tar_filename)) num++;
1197
1198 return num;
1199 }
1200
1210 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1211 {
1212 this->subdir = sd;
1213
1214 uint num = 0;
1215
1216 for (Searchpath sp : _valid_searchpaths) {
1217 /* Don't search in the working directory */
1218 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1219
1220 std::string path = FioGetDirectory(sp, sd);
1221 num += ScanPath(this, extension, path.c_str(), path.size(), recursive);
1222 }
1223
1224 if (tars && sd != NO_DIRECTORY) {
1225 for (const auto &tar : _tar_filelist[sd]) {
1226 num += ScanTar(this, extension, tar);
1227 }
1228 }
1229
1230 switch (sd) {
1231 case BASESET_DIR:
1232 num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1233 [[fallthrough]];
1234 case NEWGRF_DIR:
1235 num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1236 break;
1237
1238 default: break;
1239 }
1240
1241 return num;
1242 }
1243
1252 uint FileScanner::Scan(const char *extension, const std::string &directory, bool recursive)
1253 {
1254 std::string path(directory);
1255 AppendPathSeparator(path);
1256 return ScanPath(this, extension, path.c_str(), path.size(), recursive);
1257 }
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:1191
MatchesExtension
static bool MatchesExtension(const char *extension, const char *filename)
Helper to see whether a given filename matches the extension.
Definition: fileio.cpp:1134
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:962
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:953
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:1210
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:1085
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:756
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:1107
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:814
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:1151
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:834
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:792
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