OpenTTD Source: src/fileio.cpp Source File

OpenTTD Source 1.11.0-RC1

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 "debug.h"
13 #include "fios.h"
14 #include "string_func.h"
15 #include "tar_type.h"
16 #ifdef _WIN32
17 #include <windows.h>
18 # define access _taccess
19 #elif defined(__HAIKU__)
20 #include <Path.h>
21 #include <storage/FindDirectory.h>
22 #else
23 #include <unistd.h>
24 #include <pwd.h>
25 #endif
26 #include <sys/stat.h>
27 #include <array>
28 #include <sstream>
29
30 #include "safeguards.h"
31
33 #define FIO_BUFFER_SIZE 512
34
36 struct Fio {
37 byte *buffer, *buffer_end;
38 byte buffer_start[FIO_BUFFER_SIZE];
39 size_t pos;
40 FILE *cur_fh;
41 std::string filename;
42 std::array<FILE *, MAX_FILE_SLOTS> handles;
43 std::array<std::string, MAX_FILE_SLOTS> filenames;
44 std::array<std::string, MAX_FILE_SLOTS> shortnames;
45 };
46
47 static Fio _fio;
48
50 static bool _do_scan_working_directory = true;
51
52 extern std::string _config_file;
53 extern std::string _highscore_file;
54
59 size_t FioGetPos()
60 {
61 return _fio.pos + (_fio.buffer - _fio.buffer_end);
62 }
63
69 const char *FioGetFilename(uint8 slot)
70 {
71 return _fio.shortnames[slot].c_str();
72 }
73
79 void FioSeekTo(size_t pos, int mode)
80 {
81 if (mode == SEEK_CUR) pos += FioGetPos();
82 _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
83 _fio.pos = pos;
84 if (fseek(_fio.cur_fh, _fio.pos, SEEK_SET) < 0) {
85 DEBUG(misc, 0, "Seeking in %s failed", _fio.filename.c_str());
86 }
87 }
88
94 void FioSeekToFile(uint8 slot, size_t pos)
95 {
96 FILE *f = _fio.handles[slot];
97 assert(f != nullptr);
98 _fio.cur_fh = f;
99 _fio.filename = _fio.filenames[slot];
100 FioSeekTo(pos, SEEK_SET);
101 }
102
107 byte FioReadByte()
108 {
109 if (_fio.buffer == _fio.buffer_end) {
110 _fio.buffer = _fio.buffer_start;
111 size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
112 _fio.pos += size;
113 _fio.buffer_end = _fio.buffer_start + size;
114
115 if (size == 0) return 0;
116 }
117 return *_fio.buffer++;
118 }
119
124 void FioSkipBytes(int n)
125 {
126 for (;;) {
127 int m = std::min<int>(_fio.buffer_end - _fio.buffer, n);
128 _fio.buffer += m;
129 n -= m;
130 if (n == 0) break;
131 FioReadByte();
132 n--;
133 }
134 }
135
140 uint16 FioReadWord()
141 {
142 byte b = FioReadByte();
143 return (FioReadByte() << 8) | b;
144 }
145
150 uint32 FioReadDword()
151 {
152 uint b = FioReadWord();
153 return (FioReadWord() << 16) | b;
154 }
155
161 void FioReadBlock(void *ptr, size_t size)
162 {
163 FioSeekTo(FioGetPos(), SEEK_SET);
164 _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
165 }
166
171 static inline void FioCloseFile(int slot)
172 {
173 if (_fio.handles[slot] != nullptr) {
174 fclose(_fio.handles[slot]);
175
176 _fio.shortnames[slot].clear();
177
178 _fio.handles[slot] = nullptr;
179 }
180 }
181
183 void FioCloseAll()
184 {
185 for (int i = 0; i != lengthof(_fio.handles); i++) {
186 FioCloseFile(i);
187 }
188 }
189
196 void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir)
197 {
198 FILE *f;
199
200 f = FioFOpenFile(filename, "rb", subdir);
201 if (f == nullptr) usererror("Cannot open file '%s'", filename.c_str());
202 long pos = ftell(f);
203 if (pos < 0) usererror("Cannot read file '%s'", filename.c_str());
204
205 FioCloseFile(slot); // if file was opened before, close it
206 _fio.handles[slot] = f;
207 _fio.filenames[slot] = filename;
208
209 /* Store the filename without path and extension */
210 auto t = filename.rfind(PATHSEPCHAR);
211 std::string sn = filename.substr(t != std::string::npos ? t + 1 : 0);
212 _fio.shortnames[slot] = sn.substr(0, sn.rfind('.'));
213 strtolower(_fio.shortnames[slot]);
214
215 FioSeekToFile(slot, (size_t)pos);
216 }
217
218 static const char * const _subdirs[] = {
219 "",
220 "save" PATHSEP,
221 "save" PATHSEP "autosave" PATHSEP,
222 "scenario" PATHSEP,
223 "scenario" PATHSEP "heightmap" PATHSEP,
224 "gm" PATHSEP,
225 "data" PATHSEP,
226 "baseset" PATHSEP,
227 "newgrf" PATHSEP,
228 "lang" PATHSEP,
229 "ai" PATHSEP,
230 "ai" PATHSEP "library" PATHSEP,
231 "game" PATHSEP,
232 "game" PATHSEP "library" PATHSEP,
233 "screenshot" PATHSEP,
234 };
235 static_assert(lengthof(_subdirs) == NUM_SUBDIRS);
236
243 std::array<std::string, NUM_SEARCHPATHS> _searchpaths;
244 std::array<TarList, NUM_SUBDIRS> _tar_list;
245 TarFileList _tar_filelist[NUM_SUBDIRS];
246
247 typedef std::map<std::string, std::string> TarLinkList;
248 static TarLinkList _tar_linklist[NUM_SUBDIRS];
249
255 bool IsValidSearchPath(Searchpath sp)
256 {
257 return sp < _searchpaths.size() && !_searchpaths[sp].empty();
258 }
259
266 bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
267 {
268 FILE *f = FioFOpenFile(filename, "rb", subdir);
269 if (f == nullptr) return false;
270
271 FioFCloseFile(f);
272 return true;
273 }
274
280 bool FileExists(const std::string &filename)
281 {
282 return access(OTTD2FS(filename.c_str()), 0) == 0;
283 }
284
288 void FioFCloseFile(FILE *f)
289 {
290 fclose(f);
291 }
292
299 std::string FioFindFullPath(Subdirectory subdir, const char *filename)
300 {
301 Searchpath sp;
302 assert(subdir < NUM_SUBDIRS);
303
304 FOR_ALL_SEARCHPATHS(sp) {
305 std::string buf = FioGetDirectory(sp, subdir);
306 buf += filename;
307 if (FileExists(buf)) return buf;
308 #if !defined(_WIN32)
309 /* Be, as opening files, aware that sometimes the filename
310 * might be in uppercase when it is in lowercase on the
311 * disk. Of course Windows doesn't care about casing. */
312 if (strtolower(buf, _searchpaths[sp].size() - 1) && FileExists(buf)) return buf;
313 #endif
314 }
315
316 return {};
317 }
318
319 std::string FioGetDirectory(Searchpath sp, Subdirectory subdir)
320 {
321 assert(subdir < NUM_SUBDIRS);
322 assert(sp < NUM_SEARCHPATHS);
323
324 return _searchpaths[sp] + _subdirs[subdir];
325 }
326
327 std::string FioFindDirectory(Subdirectory subdir)
328 {
329 Searchpath sp;
330
331 /* Find and return the first valid directory */
332 FOR_ALL_SEARCHPATHS(sp) {
333 std::string ret = FioGetDirectory(sp, subdir);
334 if (FileExists(ret)) return ret;
335 }
336
337 /* Could not find the directory, fall back to a base path */
338 return _personal_dir;
339 }
340
341 static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
342 {
343 #if defined(_WIN32)
344 /* fopen is implemented as a define with ellipses for
345 * Unicode support (prepend an L). As we are not sending
346 * a string, but a variable, it 'renames' the variable,
347 * so make that variable to makes it compile happily */
348 wchar_t Lmode[5];
349 MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
350 #endif
351 FILE *f = nullptr;
352 std::string buf;
353
354 if (subdir == NO_DIRECTORY) {
355 buf = filename;
356 } else {
357 buf = _searchpaths[sp] + _subdirs[subdir] + filename;
358 }
359
360 #if defined(_WIN32)
361 if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf.c_str())) == INVALID_FILE_ATTRIBUTES) return nullptr;
362 #endif
363
364 f = fopen(buf.c_str(), mode);
365 #if !defined(_WIN32)
366 if (f == nullptr && strtolower(buf, subdir == NO_DIRECTORY ? 0 : _searchpaths[sp].size() - 1) ) {
367 f = fopen(buf.c_str(), mode);
368 }
369 #endif
370 if (f != nullptr && filesize != nullptr) {
371 /* Find the size of the file */
372 fseek(f, 0, SEEK_END);
373 *filesize = ftell(f);
374 fseek(f, 0, SEEK_SET);
375 }
376 return f;
377 }
378
386 FILE *FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
387 {
388 FILE *f = fopen(entry.tar_filename.c_str(), "rb");
389 if (f == nullptr) return f;
390
391 if (fseek(f, entry.position, SEEK_SET) < 0) {
392 fclose(f);
393 return nullptr;
394 }
395
396 if (filesize != nullptr) *filesize = entry.size;
397 return f;
398 }
399
406 FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
407 {
408 FILE *f = nullptr;
409 Searchpath sp;
410
411 assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
412
413 FOR_ALL_SEARCHPATHS(sp) {
414 f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
415 if (f != nullptr || subdir == NO_DIRECTORY) break;
416 }
417
418 /* We can only use .tar in case of data-dir, and read-mode */
419 if (f == nullptr && mode[0] == 'r' && subdir != NO_DIRECTORY) {
420 /* Filenames in tars are always forced to be lowercase */
421 std::string resolved_name = filename;
422 strtolower(resolved_name);
423
424 /* Resolve ".." */
425 std::istringstream ss(resolved_name);
426 std::vector<std::string> tokens;
427 std::string token;
428 while (std::getline(ss, token, PATHSEPCHAR)) {
429 if (token == "..") {
430 if (tokens.size() < 2) return nullptr;
431 tokens.pop_back();
432 } else {
433 tokens.push_back(token);
434 }
435 }
436
437 resolved_name.clear();
438 bool first = true;
439 for (const std::string &token : tokens) {
440 if (!first) {
441 resolved_name += PATHSEP;
442 }
443 resolved_name += token;
444 first = false;
445 }
446
447 /* Resolve ONE directory link */
448 for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
449 const std::string &src = link->first;
450 size_t len = src.length();
451 if (resolved_name.length() >= len && resolved_name[len - 1] == PATHSEPCHAR && src.compare(0, len, resolved_name, 0, len) == 0) {
452 /* Apply link */
453 resolved_name.replace(0, len, link->second);
454 break; // Only resolve one level
455 }
456 }
457
458 TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
459 if (it != _tar_filelist[subdir].end()) {
460 f = FioFOpenFileTar(it->second, filesize);
461 }
462 }
463
464 /* Sometimes a full path is given. To support
465 * the 'subdirectory' must be 'removed'. */
466 if (f == nullptr && subdir != NO_DIRECTORY) {
467 switch (subdir) {
468 case BASESET_DIR:
469 f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
470 if (f != nullptr) break;
471 FALLTHROUGH;
472 case NEWGRF_DIR:
473 f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
474 break;
475
476 default:
477 f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
478 break;
479 }
480 }
481
482 return f;
483 }
484
490 void FioCreateDirectory(const std::string &name)
491 {
492 auto p = name.find_last_of(PATHSEPCHAR);
493 if (p != std::string::npos) {
494 std::string dirname = name.substr(0, p);
495 DIR *dir = ttd_opendir(dirname.c_str());
496 if (dir == nullptr) {
497 FioCreateDirectory(dirname); // Try creating the parent directory, if we couldn't open it
498 } else {
499 closedir(dir);
500 }
501 }
502
503 /* Ignore directory creation errors; they'll surface later on, and most
504 * of the time they are 'directory already exists' errors anyhow. */
505 #if defined(_WIN32)
506 CreateDirectory(OTTD2FS(name.c_str()), nullptr);
507 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
508 mkdir(OTTD2FS(name.c_str()));
509 #else
510 mkdir(OTTD2FS(name.c_str()), 0755);
511 #endif
512 }
513
520 void AppendPathSeparator(std::string &buf)
521 {
522 if (buf.empty()) return;
523
524 if (buf.back() != PATHSEPCHAR) buf.push_back(PATHSEPCHAR);
525 }
526
527 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
528 {
529 std::string src = srcParam;
530 std::string dest = destParam;
531 /* Tar internals assume lowercase */
532 std::transform(src.begin(), src.end(), src.begin(), tolower);
533 std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
534
535 TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
536 if (dest_file != _tar_filelist[subdir].end()) {
537 /* Link to file. Process the link like the destination file. */
538 _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
539 } else {
540 /* Destination file not found. Assume 'link to directory'
541 * Append PATHSEPCHAR to 'src' and 'dest' if needed */
542 const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
543 const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
544 _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
545 }
546 }
547
553 static void SimplifyFileName(char *name)
554 {
555 /* Force lowercase */
556 strtolower(name);
557
558 /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
559 #if (PATHSEPCHAR != '/')
560 for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
561 #endif
562 }
563
569 uint TarScanner::DoScan(Subdirectory sd)
570 {
571 _tar_filelist[sd].clear();
572 _tar_list[sd].clear();
573 uint num = this->Scan(".tar", sd, false);
574 if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
575 return num;
576 }
577
578 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
579 {
580 DEBUG(misc, 1, "Scanning for tars");
581 TarScanner fs;
582 uint num = 0;
583 if (mode & TarScanner::BASESET) {
584 num += fs.DoScan(BASESET_DIR);
585 }
586 if (mode & TarScanner::NEWGRF) {
587 num += fs.DoScan(NEWGRF_DIR);
588 }
589 if (mode & TarScanner::AI) {
590 num += fs.DoScan(AI_DIR);
591 num += fs.DoScan(AI_LIBRARY_DIR);
592 }
593 if (mode & TarScanner::GAME) {
594 num += fs.DoScan(GAME_DIR);
595 num += fs.DoScan(GAME_LIBRARY_DIR);
596 }
597 if (mode & TarScanner::SCENARIO) {
598 num += fs.DoScan(SCENARIO_DIR);
599 num += fs.DoScan(HEIGHTMAP_DIR);
600 }
601 DEBUG(misc, 1, "Scan complete, found %d files", num);
602 return num;
603 }
604
611 bool TarScanner::AddFile(Subdirectory sd, const std::string &filename)
612 {
613 this->subdir = sd;
614 return this->AddFile(filename, 0);
615 }
616
617 bool TarScanner::AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename)
618 {
619 /* No tar within tar. */
620 assert(tar_filename.empty());
621
622 /* The TAR-header, repeated for every file */
623 struct TarHeader {
624 char name[100];
625 char mode[8];
626 char uid[8];
627 char gid[8];
628 char size[12];
629 char mtime[12];
630 char chksum[8];
631 char typeflag;
632 char linkname[100];
633 char magic[6];
634 char version[2];
635 char uname[32];
636 char gname[32];
637 char devmajor[8];
638 char devminor[8];
639 char prefix[155];
640
641 char unused[12];
642 };
643
644 /* Check if we already seen this file */
645 TarList::iterator it = _tar_list[this->subdir].find(filename);
646 if (it != _tar_list[this->subdir].end()) return false;
647
648 FILE *f = fopen(filename.c_str(), "rb");
649 /* Although the file has been found there can be
650 * a number of reasons we cannot open the file.
651 * Most common case is when we simply have not
652 * been given read access. */
653 if (f == nullptr) return false;
654
655 _tar_list[this->subdir][filename] = std::string{};
656
657 TarLinkList links;
658
659 TarHeader th;
660 char buf[sizeof(th.name) + 1], *end;
661 char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
662 char link[sizeof(th.linkname) + 1];
663 char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
664 size_t num = 0, pos = 0;
665
666 /* Make a char of 512 empty bytes */
667 char empty[512];
668 memset(&empty[0], 0, sizeof(empty));
669
670 for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
671 size_t num_bytes_read = fread(&th, 1, 512, f);
672 if (num_bytes_read != 512) break;
673 pos += num_bytes_read;
674
675 /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
676 if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
677 /* If we have only zeros in the block, it can be an end-of-file indicator */
678 if (memcmp(&th, &empty[0], 512) == 0) continue;
679
680 DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename.c_str());
681 fclose(f);
682 return false;
683 }
684
685 name[0] = '\0';
686
687 /* The prefix contains the directory-name */
688 if (th.prefix[0] != '\0') {
689 strecpy(name, th.prefix, lastof(name));
690 strecat(name, PATHSEP, lastof(name));
691 }
692
693 /* Copy the name of the file in a safe way at the end of 'name' */
694 strecat(name, th.name, lastof(name));
695
696 /* Calculate the size of the file.. for some strange reason this is stored as a string */
697 strecpy(buf, th.size, lastof(buf));
698 size_t skip = strtoul(buf, &end, 8);
699
700 switch (th.typeflag) {
701 case '\0':
702 case '0': { // regular file
703 /* Ignore empty files */
704 if (skip == 0) break;
705
706 if (strlen(name) == 0) break;
707
708 /* Store this entry in the list */
709 TarFileListEntry entry;
710 entry.tar_filename = filename;
711 entry.size = skip;
712 entry.position = pos;
713
714 /* Convert to lowercase and our PATHSEPCHAR */
715 SimplifyFileName(name);
716
717 DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
718 if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
719
720 break;
721 }
722
723 case '1': // hard links
724 case '2': { // symbolic links
725 /* Copy the destination of the link in a safe way at the end of 'linkname' */
726 strecpy(link, th.linkname, lastof(link));
727
728 if (strlen(name) == 0 || strlen(link) == 0) break;
729
730 /* Convert to lowercase and our PATHSEPCHAR */
731 SimplifyFileName(name);
732 SimplifyFileName(link);
733
734 /* Only allow relative links */
735 if (link[0] == PATHSEPCHAR) {
736 DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
737 break;
738 }
739
740 /* Process relative path.
741 * Note: The destination of links must not contain any directory-links. */
742 strecpy(dest, name, lastof(dest));
743 char *destpos = strrchr(dest, PATHSEPCHAR);
744 if (destpos == nullptr) destpos = dest;
745 *destpos = '\0';
746
747 char *pos = link;
748 while (*pos != '\0') {
749 char *next = strchr(pos, PATHSEPCHAR);
750 if (next == nullptr) {
751 next = pos + strlen(pos);
752 } else {
753 /* Terminate the substring up to the path separator character. */
754 *next++= '\0';
755 }
756
757 if (strcmp(pos, ".") == 0) {
758 /* Skip '.' (current dir) */
759 } else if (strcmp(pos, "..") == 0) {
760 /* level up */
761 if (dest[0] == '\0') {
762 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
763 break;
764 }
765
766 /* Truncate 'dest' after last PATHSEPCHAR.
767 * This assumes that the truncated part is a real directory and not a link. */
768 destpos = strrchr(dest, PATHSEPCHAR);
769 if (destpos == nullptr) destpos = dest;
770 *destpos = '\0';
771 } else {
772 /* Append at end of 'dest' */
773 if (destpos != dest) destpos = strecpy(destpos, PATHSEP, lastof(dest));
774 destpos = strecpy(destpos, pos, lastof(dest));
775 }
776
777 if (destpos >= lastof(dest)) {
778 DEBUG(misc, 0, "The length of a link in tar-file '%s' is too large (malformed?)", filename.c_str());
779 fclose(f);
780 return false;
781 }
782
783 pos = next;
784 }
785
786 /* Store links in temporary list */
787 DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
788 links.insert(TarLinkList::value_type(name, dest));
789
790 break;
791 }
792
793 case '5': // directory
794 /* Convert to lowercase and our PATHSEPCHAR */
795 SimplifyFileName(name);
796
797 /* Store the first directory name we detect */
798 DEBUG(misc, 6, "Found dir in tar: %s", name);
799 if (_tar_list[this->subdir][filename].empty()) _tar_list[this->subdir][filename] = name;
800 break;
801
802 default:
803 /* Ignore other types */
804 break;
805 }
806
807 /* Skip to the next block.. */
808 skip = Align(skip, 512);
809 if (fseek(f, skip, SEEK_CUR) < 0) {
810 DEBUG(misc, 0, "The file '%s' can't be read as a valid tar-file", filename.c_str());
811 fclose(f);
812 return false;
813 }
814 pos += skip;
815 }
816
817 DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename.c_str(), num);
818 fclose(f);
819
820 /* Resolve file links and store directory links.
821 * We restrict usage of links to two cases:
822 * 1) Links to directories:
823 * Both the source path and the destination path must NOT contain any further links.
824 * When resolving files at most one directory link is resolved.
825 * 2) Links to files:
826 * The destination path must NOT contain any links.
827 * The source path may contain one directory link.
828 */
829 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
830 const std::string &src = link->first;
831 const std::string &dest = link->second;
832 TarAddLink(src, dest, this->subdir);
833 }
834
835 return true;
836 }
837
845 bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
846 {
847 TarList::iterator it = _tar_list[subdir].find(tar_filename);
848 /* We don't know the file. */
849 if (it == _tar_list[subdir].end()) return false;
850
851 const auto &dirname = (*it).second;
852
853 /* The file doesn't have a sub directory! */
854 if (dirname.empty()) {
855 DEBUG(misc, 1, "Extracting %s failed; archive rejected, the contents must be in a sub directory", tar_filename.c_str());
856 return false;
857 }
858
859 std::string filename = tar_filename;
860 auto p = filename.find_last_of(PATHSEPCHAR);
861 /* The file's path does not have a separator? */
862 if (p == std::string::npos) return false;
863
864 filename.replace(p + 1, std::string::npos, dirname);
865 DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename.c_str(), filename.c_str());
866 FioCreateDirectory(filename);
867
868 for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
869 if (tar_filename != it2->second.tar_filename) continue;
870
871 filename.replace(p + 1, std::string::npos, it2->first);
872
873 DEBUG(misc, 9, " extracting %s", filename.c_str());
874
875 /* First open the file in the .tar. */
876 size_t to_copy = 0;
877 std::unique_ptr<FILE, FileDeleter> in(FioFOpenFileTar(it2->second, &to_copy));
878 if (!in) {
879 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename.c_str(), tar_filename.c_str());
880 return false;
881 }
882
883 /* Now open the 'output' file. */
884 std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(), "wb"));
885 if (!out) {
886 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename.c_str(), filename.c_str());
887 return false;
888 }
889
890 /* Now read from the tar and write it into the file. */
891 char buffer[4096];
892 size_t read;
893 for (; to_copy != 0; to_copy -= read) {
894 read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), in.get());
895 if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read) break;
896 }
897
898 if (to_copy != 0) {
899 DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename.c_str(), (int)to_copy);
900 return false;
901 }
902 }
903
904 DEBUG(misc, 9, " extraction successful");
905 return true;
906 }
907
908 #if defined(_WIN32)
909
914 extern void DetermineBasePaths(const char *exe);
915 #else /* defined(_WIN32) */
916
924 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
925 {
926 char tmp[MAX_PATH];
927 strecpy(tmp, exe, lastof(tmp));
928
929 bool success = false;
930 #ifdef WITH_COCOA
931 char *app_bundle = strchr(tmp, '.');
932 while (app_bundle != nullptr && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
933
934 if (app_bundle != nullptr) *app_bundle = '\0';
935 #endif /* WITH_COCOA */
936 char *s = strrchr(tmp, PATHSEPCHAR);
937 if (s != nullptr) {
938 *s = '\0';
939 if (chdir(tmp) != 0) {
940 DEBUG(misc, 0, "Directory with the binary does not exist?");
941 } else {
942 success = true;
943 }
944 }
945 return success;
946 }
947
958 bool DoScanWorkingDirectory()
959 {
960 /* No working directory, so nothing to do. */
961 if (_searchpaths[SP_WORKING_DIR].empty()) return false;
962
963 /* Working directory is root, so do nothing. */
964 if (_searchpaths[SP_WORKING_DIR] == PATHSEP) return false;
965
966 /* No personal/home directory, so the working directory won't be that. */
967 if (_searchpaths[SP_PERSONAL_DIR].empty()) return true;
968
969 std::string tmp = _searchpaths[SP_WORKING_DIR] + PERSONAL_DIR;
970 AppendPathSeparator(tmp);
971
972 return _searchpaths[SP_PERSONAL_DIR] != tmp;
973 }
974
980 static std::string GetHomeDir()
981 {
982 #ifdef __HAIKU__
983 BPath path;
984 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
985 return std::string(path.Path());
986 #else
987 const char *home_env = getenv("HOME"); // Stack var, shouldn't be freed
988 if (home_env != nullptr) return std::string(home_env);
989
990 const struct passwd *pw = getpwuid(getuid());
991 if (pw != nullptr) return std::string(pw->pw_dir);
992 #endif
993 return {};
994 }
995
1000 void DetermineBasePaths(const char *exe)
1001 {
1002 std::string tmp;
1003 const std::string homedir = GetHomeDir();
1004 #ifdef USE_XDG
1005 const char *xdg_data_home = getenv("XDG_DATA_HOME");
1006 if (xdg_data_home != nullptr) {
1007 tmp = xdg_data_home;
1008 tmp += PATHSEP;
1009 tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1010 AppendPathSeparator(tmp);
1011 _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
1012
1013 tmp += "content_download";
1014 AppendPathSeparator(tmp);
1015 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR_XDG] = tmp;
1016 } else if (!homedir.empty()) {
1017 tmp = homedir;
1018 tmp += PATHSEP ".local" PATHSEP "share" PATHSEP;
1019 tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1020 AppendPathSeparator(tmp);
1021 _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
1022
1023 tmp += "content_download";
1024 AppendPathSeparator(tmp);
1025 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR_XDG] = tmp;
1026 } else {
1027 _searchpaths[SP_PERSONAL_DIR_XDG].clear();
1028 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR_XDG].clear();
1029 }
1030 #endif
1031
1032 #if defined(OS2) || !defined(WITH_PERSONAL_DIR)
1033 _searchpaths[SP_PERSONAL_DIR].clear();
1034 #else
1035 if (!homedir.empty()) {
1036 tmp = homedir;
1037 tmp += PATHSEP;
1038 tmp += PERSONAL_DIR;
1039 AppendPathSeparator(tmp);
1040 _searchpaths[SP_PERSONAL_DIR] = tmp;
1041
1042 tmp += "content_download";
1043 AppendPathSeparator(tmp);
1044 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR] = tmp;
1045 } else {
1046 _searchpaths[SP_PERSONAL_DIR].clear();
1047 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR].clear();
1048 }
1049 #endif
1050
1051 #if defined(WITH_SHARED_DIR)
1052 tmp = SHARED_DIR;
1053 AppendPathSeparator(tmp);
1054 _searchpaths[SP_SHARED_DIR] = tmp;
1055 #else
1056 _searchpaths[SP_SHARED_DIR].clear();
1057 #endif
1058
1059 char cwd[MAX_PATH];
1060 if (getcwd(cwd, MAX_PATH) == nullptr) *cwd = '\0';
1061
1062 if (_config_file.empty()) {
1063 /* Get the path to working directory of OpenTTD. */
1064 tmp = cwd;
1065 AppendPathSeparator(tmp);
1066 _searchpaths[SP_WORKING_DIR] = tmp;
1067
1068 _do_scan_working_directory = DoScanWorkingDirectory();
1069 } else {
1070 /* Use the folder of the config file as working directory. */
1071 size_t end = _config_file.find_last_of(PATHSEPCHAR);
1072 if (end == std::string::npos) {
1073 /* _config_file is not in a folder, so use current directory. */
1074 tmp = cwd;
1075 AppendPathSeparator(tmp);
1076 _searchpaths[SP_WORKING_DIR] = tmp;
1077 } else {
1078 _searchpaths[SP_WORKING_DIR] = _config_file.substr(0, end + 1);
1079 }
1080 }
1081
1082 /* Change the working directory to that one of the executable */
1083 if (ChangeWorkingDirectoryToExecutable(exe)) {
1084 char buf[MAX_PATH];
1085 if (getcwd(buf, lengthof(buf)) == nullptr) {
1086 tmp.clear();
1087 } else {
1088 tmp = buf;
1089 }
1090 AppendPathSeparator(tmp);
1091 _searchpaths[SP_BINARY_DIR] = tmp;
1092 } else {
1093 _searchpaths[SP_BINARY_DIR].clear();
1094 }
1095
1096 if (cwd[0] != '\0') {
1097 /* Go back to the current working directory. */
1098 if (chdir(cwd) != 0) {
1099 DEBUG(misc, 0, "Failed to return to working directory!");
1100 }
1101 }
1102
1103 #if !defined(GLOBAL_DATA_DIR)
1104 _searchpaths[SP_INSTALLATION_DIR].clear();
1105 #else
1106 tmp = GLOBAL_DATA_DIR;
1107 AppendPathSeparator(tmp);
1108 _searchpaths[SP_INSTALLATION_DIR] = tmp;
1109 #endif
1110 #ifdef WITH_COCOA
1111 extern void CocoaSetApplicationBundleDir();
1112 CocoaSetApplicationBundleDir();
1113 #else
1114 _searchpaths[SP_APPLICATION_BUNDLE_DIR].clear();
1115 #endif
1116 }
1117 #endif /* defined(_WIN32) */
1118
1119 std::string _personal_dir;
1120
1127 void DeterminePaths(const char *exe)
1128 {
1129 DetermineBasePaths(exe);
1130
1131 #ifdef USE_XDG
1132 std::string config_home;
1133 const std::string homedir = GetHomeDir();
1134 const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
1135 if (xdg_config_home != nullptr) {
1136 config_home = xdg_config_home;
1137 config_home += PATHSEP;
1138 config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1139 } else if (!homedir.empty()) {
1140 /* Defaults to ~/.config */
1141 config_home = homedir;
1142 config_home += PATHSEP ".config" PATHSEP;
1143 config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1144 }
1145 AppendPathSeparator(config_home);
1146 #endif
1147
1148 Searchpath sp;
1149 FOR_ALL_SEARCHPATHS(sp) {
1150 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1151 DEBUG(misc, 4, "%s added as search path", _searchpaths[sp].c_str());
1152 }
1153
1154 std::string config_dir;
1155 if (!_config_file.empty()) {
1156 config_dir = _searchpaths[SP_WORKING_DIR];
1157 } else {
1158 std::string personal_dir = FioFindFullPath(BASE_DIR, "openttd.cfg");
1159 if (!personal_dir.empty()) {
1160 auto end = personal_dir.find_last_of(PATHSEPCHAR);
1161 if (end != std::string::npos) personal_dir.erase(end + 1);
1162 config_dir = personal_dir;
1163 } else {
1164 #ifdef USE_XDG
1165 /* No previous configuration file found. Use the configuration folder from XDG. */
1166 config_dir = config_home;
1167 #else
1168 static const Searchpath new_openttd_cfg_order[] = {
1169 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
1170 };
1171
1172 config_dir.clear();
1173 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1174 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1175 config_dir = _searchpaths[new_openttd_cfg_order[i]];
1176 break;
1177 }
1178 }
1179 #endif
1180 }
1181 _config_file = config_dir + "openttd.cfg";
1182 }
1183
1184 DEBUG(misc, 3, "%s found as config directory", config_dir.c_str());
1185
1186 _highscore_file = config_dir + "hs.dat";
1187 extern std::string _hotkeys_file;
1188 _hotkeys_file = config_dir + "hotkeys.cfg";
1189 extern std::string _windows_file;
1190 _windows_file = config_dir + "windows.cfg";
1191
1192 #ifdef USE_XDG
1193 if (config_dir == config_home) {
1194 /* We are using the XDG configuration home for the config file,
1195 * then store the rest in the XDG data home folder. */
1196 _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1197 } else
1198 #endif
1199 {
1200 _personal_dir = config_dir;
1201 }
1202
1203 /* Make the necessary folders */
1204 FioCreateDirectory(config_dir);
1205 #if defined(WITH_PERSONAL_DIR)
1206 FioCreateDirectory(_personal_dir);
1207 #endif
1208
1209 DEBUG(misc, 3, "%s found as personal directory", _personal_dir.c_str());
1210
1211 static const Subdirectory default_subdirs[] = {
1212 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR
1213 };
1214
1215 for (uint i = 0; i < lengthof(default_subdirs); i++) {
1216 FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
1217 }
1218
1219 /* If we have network we make a directory for the autodownloading of content */
1220 _searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
1221 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
1222
1223 /* Create the directory for each of the types of content */
1224 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };
1225 for (uint i = 0; i < lengthof(dirs); i++) {
1226 FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
1227 }
1228
1229 extern std::string _log_file;
1230 _log_file = _personal_dir + "openttd.log";
1231 }
1232
1237 void SanitizeFilename(char *filename)
1238 {
1239 for (; *filename != '\0'; filename++) {
1240 switch (*filename) {
1241 /* The following characters are not allowed in filenames
1242 * on at least one of the supported operating systems: */
1243 case ':': case '\\': case '*': case '?': case '/':
1244 case '<': case '>': case '|': case '"':
1245 *filename = '_';
1246 break;
1247 }
1248 }
1249 }
1250
1259 std::unique_ptr<char> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
1260 {
1261 FILE *in = fopen(filename.c_str(), "rb");
1262 if (in == nullptr) return nullptr;
1263
1264 FileCloser fc(in);
1265
1266 fseek(in, 0, SEEK_END);
1267 size_t len = ftell(in);
1268 fseek(in, 0, SEEK_SET);
1269 if (len > maxsize) return nullptr;
1270
1271 /* std::unique_ptr assumes new/delete unless a custom deleter is supplied.
1272 * As we don't want to have to carry that deleter all over the place, use
1273 * new directly to allocate the memory instead of malloc. */
1274 std::unique_ptr<char> mem(static_cast<char *>(::operator new(len + 1)));
1275
1276 mem.get()[len] = 0;
1277 if (fread(mem.get(), len, 1, in) != 1) return nullptr;
1278
1279 lenp = len;
1280 return mem;
1281 }
1282
1289 static bool MatchesExtension(const char *extension, const char *filename)
1290 {
1291 if (extension == nullptr) return true;
1292
1293 const char *ext = strrchr(filename, extension[0]);
1294 return ext != nullptr && strcasecmp(ext, extension) == 0;
1295 }
1296
1306 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1307 {
1308 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
1309
1310 uint num = 0;
1311 struct stat sb;
1312 struct dirent *dirent;
1313 DIR *dir;
1314
1315 if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1316
1317 while ((dirent = readdir(dir)) != nullptr) {
1318 const char *d_name = FS2OTTD(dirent->d_name);
1319
1320 if (!FiosIsValidFile(path, dirent, &sb)) continue;
1321
1322 std::string filename(path);
1323 filename += d_name;
1324
1325 if (S_ISDIR(sb.st_mode)) {
1326 /* Directory */
1327 if (!recursive) continue;
1328 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
1329 AppendPathSeparator(filename);
1330 num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
1331 } else if (S_ISREG(sb.st_mode)) {
1332 /* File */
1333 if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, basepath_length, {})) num++;
1334 }
1335 }
1336
1337 closedir(dir);
1338
1339 return num;
1340 }
1341
1348 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
1349 {
1350 uint num = 0;
1351 const auto &filename = (*tar).first;
1352
1353 if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
1354
1355 return num;
1356 }
1357
1367 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1368 {
1369 this->subdir = sd;
1370
1371 Searchpath sp;
1372 TarFileList::iterator tar;
1373 uint num = 0;
1374
1375 FOR_ALL_SEARCHPATHS(sp) {
1376 /* Don't search in the working directory */
1377 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1378
1379 std::string path = FioGetDirectory(sp, sd);
1380 num += ScanPath(this, extension, path.c_str(), path.size(), recursive);
1381 }
1382
1383 if (tars && sd != NO_DIRECTORY) {
1384 FOR_ALL_TARS(tar, sd) {
1385 num += ScanTar(this, extension, tar);
1386 }
1387 }
1388
1389 switch (sd) {
1390 case BASESET_DIR:
1391 num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1392 FALLTHROUGH;
1393 case NEWGRF_DIR:
1394 num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1395 break;
1396
1397 default: break;
1398 }
1399
1400 return num;
1401 }
1402
1411 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
1412 {
1413 std::string path(directory);
1414 AppendPathSeparator(path);
1415 return ScanPath(this, extension, path.c_str(), path.size(), recursive);
1416 }
ReadFileToMem
std::unique_ptr< char > ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
Load a file into memory.
Definition: fileio.cpp:1259
MatchesExtension
static bool MatchesExtension(const char *extension, const char *filename)
Helper to see whether a given filename matches the extension.
Definition: fileio.cpp:1289
SP_AUTODOWNLOAD_DIR
@ SP_AUTODOWNLOAD_DIR
Search within the autodownload directory.
Definition: fileio_type.h:142
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:845
SAVE_DIR
@ SAVE_DIR
Base directory for all savegames.
Definition: fileio_type.h:110
FioReadBlock
void FioReadBlock(void *ptr, size_t size)
Read a block.
Definition: fileio.cpp:161
usererror
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
Definition: openttd.cpp:102
_tar_linklist
static TarLinkList _tar_linklist[NUM_SUBDIRS]
List of directory links.
Definition: fileio.cpp:248
FioReadWord
uint16 FioReadWord()
Read a word (16 bits) from the file (in low endian format).
Definition: fileio.cpp:140
FS2OTTD
const char * FS2OTTD(const wchar_t *name)
Convert to OpenTTD's encoding from wide characters.
Definition: win32.cpp:565
_personal_dir
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:1119
strtolower
bool strtolower(char *str)
Convert a given ASCII string to lowercase.
Definition: string.cpp:354
SP_PERSONAL_DIR
@ SP_PERSONAL_DIR
Search in the personal directory.
Definition: fileio_type.h:137
FioOpenFile
void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir)
Open a slotted file.
Definition: fileio.cpp:196
Fio::pos
size_t pos
current (system) position in file
Definition: fileio.cpp:39
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:569
GAME_LIBRARY_DIR
@ GAME_LIBRARY_DIR
Subdirectory for all GS libraries.
Definition: fileio_type.h:122
SCREENSHOT_DIR
@ SCREENSHOT_DIR
Subdirectory for all screenshots.
Definition: fileio_type.h:123
SP_AUTODOWNLOAD_PERSONAL_DIR
@ SP_AUTODOWNLOAD_PERSONAL_DIR
Search within the autodownload directory located in the personal directory.
Definition: fileio_type.h:143
Searchpath
Searchpath
Types of searchpaths OpenTTD might use.
Definition: fileio_type.h:131
NUM_SUBDIRS
@ NUM_SUBDIRS
Number of subdirectories.
Definition: fileio_type.h:124
FileScanner::Scan
uint Scan(const char *extension, Subdirectory sd, bool tars=true, bool recursive=true)
Scan for files with the given extension in the given search path.
Definition: fileio.cpp:1367
DeterminePaths
void DeterminePaths(const char *exe)
Acquire the base paths (personal dir and game data dir), fill all other paths (save dir,...
Definition: fileio.cpp:1127
Fio::buffer_start
byte buffer_start[FIO_BUFFER_SIZE]
local buffer when read from file
Definition: fileio.cpp:38
HEIGHTMAP_DIR
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
Definition: fileio_type.h:113
SP_AUTODOWNLOAD_PERSONAL_DIR_XDG
@ SP_AUTODOWNLOAD_PERSONAL_DIR_XDG
Search within the autodownload directory located in the personal directory (XDG variant)
Definition: fileio_type.h:144
FOR_ALL_SEARCHPATHS
#define FOR_ALL_SEARCHPATHS(sp)
Iterator for all the search paths.
Definition: fileio_func.h:37
fileio_func.h
SP_INSTALLATION_DIR
@ SP_INSTALLATION_DIR
Search in the installation directory.
Definition: fileio_type.h:140
AUTOSAVE_DIR
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
Definition: fileio_type.h:111
Fio::filenames
std::array< std::string, MAX_FILE_SLOTS > filenames
array of filenames we (should) have open
Definition: fileio.cpp:43
Fio::buffer_end
byte * buffer_end
position pointer in local buffer and last valid byte of buffer
Definition: fileio.cpp:37
fios.h
FioFOpenFileTar
FILE * FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
Opens a file from inside a tar archive.
Definition: fileio.cpp:386
OLD_GM_DIR
@ OLD_GM_DIR
Old subdirectory for the music.
Definition: fileio_type.h:114
FioReadDword
uint32 FioReadDword()
Read a double word (32 bits) from the file (in low endian format).
Definition: fileio.cpp:150
FileCloser
Auto-close a file upon scope exit.
Definition: fileio_func.h:140
FioSkipBytes
void FioSkipBytes(int n)
Skip n bytes ahead in the file.
Definition: fileio.cpp:124
ScanTar
static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
Scan the given tar and add graphics sets when it finds one.
Definition: fileio.cpp:1348
BASE_DIR
@ BASE_DIR
Base directory for all subdirectories.
Definition: fileio_type.h:109
Align
static T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Definition: math_func.hpp:35
_searchpaths
std::array< std::string, NUM_SEARCHPATHS > _searchpaths
The search paths OpenTTD could search through.
Definition: fileio.cpp:235
Fio::cur_fh
FILE * cur_fh
current file handle
Definition: fileio.cpp:40
AI_DIR
@ AI_DIR
Subdirectory for all AI files.
Definition: fileio_type.h:119
DEBUG
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:35
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:406
_do_scan_working_directory
static bool _do_scan_working_directory
Whether the working directory should be scanned.
Definition: fileio.cpp:50
_log_file
std::string _log_file
File to reroute output of a forked OpenTTD to.
Definition: dedicated.cpp:14
tar_type.h
FileExists
bool FileExists(const std::string &filename)
Test whether the given filename exists.
Definition: fileio.cpp:280
Fio::shortnames
std::array< std::string, MAX_FILE_SLOTS > shortnames
array of short names for spriteloader's use
Definition: fileio.cpp:44
FioCloseAll
void FioCloseAll()
Close all slotted open files.
Definition: fileio.cpp:183
_fio
static Fio _fio
Fio instance.
Definition: fileio.cpp:47
ChangeWorkingDirectoryToExecutable
static bool ChangeWorkingDirectoryToExecutable(const char *exe)
Changes the working directory to the path of the give executable.
Definition: fileio.cpp:924
FileScanner::AddFile
virtual bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename)=0
Add a file with the given filename.
SP_APPLICATION_BUNDLE_DIR
@ SP_APPLICATION_BUNDLE_DIR
Search within the application bundle.
Definition: fileio_type.h:141
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:61
TarScanner::NEWGRF
@ NEWGRF
Scan for non-base sets.
Definition: fileio_func.h:88
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:490
SP_SHARED_DIR
@ SP_SHARED_DIR
Search in the shared directory, like 'Shared Files' under Windows.
Definition: fileio_type.h:138
FioCloseFile
static void FioCloseFile(int slot)
Close the file at the given slot number.
Definition: fileio.cpp:171
stdafx.h
FioSeekTo
void FioSeekTo(size_t pos, int mode)
Seek in the current file.
Definition: fileio.cpp:79
SP_WORKING_DIR
@ SP_WORKING_DIR
Search in the working directory.
Definition: fileio_type.h:133
NEWGRF_DIR
@ NEWGRF_DIR
Subdirectory for all NewGRFs.
Definition: fileio_type.h:117
AppendPathSeparator
void AppendPathSeparator(std::string &buf)
Appends, if necessary, the path separator character to the end of the string.
Definition: fileio.cpp:520
TarScanner::GAME
@ GAME
Scan for game scripts.
Definition: fileio_func.h:91
TarScanner::AddFile
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename={}) override
Add a file with the given filename.
Definition: fileio.cpp:617
FioGetPos
size_t FioGetPos()
Get position in the current file.
Definition: fileio.cpp:59
FioReadByte
byte FioReadByte()
Read a byte from the file.
Definition: fileio.cpp:107
string_func.h
_windows_file
std::string _windows_file
Config file to store WindowDesc.
Definition: window.cpp:89
SP_BINARY_DIR
@ SP_BINARY_DIR
Search in the directory where the binary resides.
Definition: fileio_type.h:139
TarScanner::SCENARIO
@ SCENARIO
Scan for scenarios and heightmaps.
Definition: fileio_func.h:90
IsValidSearchPath
bool IsValidSearchPath(Searchpath sp)
Checks whether the given search path is a valid search path.
Definition: fileio.cpp:255
_highscore_file
std::string _highscore_file
The file to store the highscore data in.
Definition: highscore.cpp:23
FioCheckFileExists
bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
Check whether the given file exists.
Definition: fileio.cpp:266
NO_DIRECTORY
@ NO_DIRECTORY
A path without any base directory.
Definition: fileio_type.h:125
TarScanner::AI
@ AI
Scan for AIs and its libraries.
Definition: fileio_func.h:89
GetHomeDir
static std::string GetHomeDir()
Gets the home directory of the user.
Definition: fileio.cpp:980
Fio::handles
std::array< FILE *, MAX_FILE_SLOTS > handles
array of file handles we can have open
Definition: fileio.cpp:42
DIR
Definition: win32.cpp:92
SanitizeFilename
void SanitizeFilename(char *filename)
Sanitizes a filename, i.e.
Definition: fileio.cpp:1237
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:1306
SimplifyFileName
static void SimplifyFileName(char *name)
Simplify filenames from tars.
Definition: fileio.cpp:553
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:1000
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:81
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:367
FileScanner
Helper for scanning for files with a given name.
Definition: fileio_func.h:59
TarScanner::BASESET
@ BASESET
Scan for base sets.
Definition: fileio_func.h:87
FioSeekToFile
void FioSeekToFile(uint8 slot, size_t pos)
Switch to a different file and seek to a position.
Definition: fileio.cpp:94
OLD_DATA_DIR
@ OLD_DATA_DIR
Old subdirectory for the data.
Definition: fileio_type.h:115
FioFindFullPath
std::string FioFindFullPath(Subdirectory subdir, const char *filename)
Find a path to the filename in one of the search directories.
Definition: fileio.cpp:299
strecpy
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: string.cpp:112
ttd_opendir
static DIR * ttd_opendir(const char *path)
A wrapper around opendir() which will convert the string from OPENTTD encoding to that of the filesys...
Definition: fileio_func.h:133
strecat
char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
Definition: string.cpp:84
TarScanner::Mode
Mode
The mode of tar scanning.
Definition: fileio_func.h:85
FIO_BUFFER_SIZE
#define FIO_BUFFER_SIZE
Size of the Fio data buffer.
Definition: fileio.cpp:33
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:383
Fio
Structure for keeping several open files with just one data buffer.
Definition: fileio.cpp:36
OTTD2FS
const wchar_t * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD's encoding to wide characters.
Definition: win32.cpp:580
DoScanWorkingDirectory
bool DoScanWorkingDirectory()
Whether we should scan the working directory.
Definition: fileio.cpp:958
Fio::filename
std::string filename
current filename
Definition: fileio.cpp:41
FioFCloseFile
void FioFCloseFile(FILE *f)
Close a file in a safe way.
Definition: fileio.cpp:288
debug.h
FioGetFilename
const char * FioGetFilename(uint8 slot)
Get the filename associated with a slot.
Definition: fileio.cpp:69
_config_file
std::string _config_file
Configuration file of OpenTTD.
Definition: settings.cpp:83
TarFileListEntry
Definition: tar_type.h:20