OpenTTD Source: src/fileio.cpp Source File

OpenTTD Source 14.1

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