4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file fileio.cpp Standard In/Out file operations */
13 #include "fileio_func.h"
20 # define access _taccess
21 #elif defined(__HAIKU__)
23 #include <storage/FindDirectory.h>
31 #ifdef WITH_XDG_BASEDIR
35 /** Size of the #Fio data buffer. */
36 #define FIO_BUFFER_SIZE 512
38 /** Structure for keeping several open files with just one data buffer. */
40 byte
*buffer
, *buffer_end
; ///< position pointer in local buffer and last valid byte of buffer
41 size_t pos
; ///< current (system) position in file
42 FILE *cur_fh
; ///< current file handle
43 const char *filename
; ///< current filename
44 FILE *handles
[MAX_FILE_SLOTS
]; ///< array of file handles we can have open
45 byte buffer_start
[FIO_BUFFER_SIZE
]; ///< local buffer when read from file
46 const char *filenames
[MAX_FILE_SLOTS
]; ///< array of filenames we (should) have open
47 char *shortnames
[MAX_FILE_SLOTS
]; ///< array of short names for spriteloader's use
48 #if defined(LIMITED_FDS)
49 uint open_handles
; ///< current amount of open handles
50 uint usage_count
[MAX_FILE_SLOTS
]; ///< count how many times this file has been opened
51 #endif /* LIMITED_FDS */
54 static Fio _fio
; ///< #Fio instance.
56 /** Whether the working directory should be scanned. */
57 static bool _do_scan_working_directory
= true;
59 extern char *_config_file
;
60 extern char *_highscore_file
;
63 * Get position in the current file.
64 * @return Position in the file.
68 return _fio
.pos
+ (_fio
.buffer
- _fio
.buffer_end
);
72 * Get the filename associated with a slot.
73 * @param slot Index of queried file.
74 * @return Name of the file.
76 const char *FioGetFilename(uint8 slot
)
78 return _fio
.shortnames
[slot
];
82 * Seek in the current file.
83 * @param pos New position.
84 * @param mode Type of seek (\c SEEK_CUR means \a pos is relative to current position, \c SEEK_SET means \a pos is absolute).
86 void FioSeekTo(size_t pos
, int mode
)
88 if (mode
== SEEK_CUR
) pos
+= FioGetPos();
89 _fio
.buffer
= _fio
.buffer_end
= _fio
.buffer_start
+ FIO_BUFFER_SIZE
;
91 if (fseek(_fio
.cur_fh
, _fio
.pos
, SEEK_SET
) < 0) {
92 DEBUG(misc
, 0, "Seeking in %s failed", _fio
.filename
);
96 #if defined(LIMITED_FDS)
97 static void FioRestoreFile(int slot
)
99 /* Do we still have the file open, or should we reopen it? */
100 if (_fio
.handles
[slot
] == NULL
) {
101 DEBUG(misc
, 6, "Restoring file '%s' in slot '%d' from disk", _fio
.filenames
[slot
], slot
);
102 FioOpenFile(slot
, _fio
.filenames
[slot
]);
104 _fio
.usage_count
[slot
]++;
106 #endif /* LIMITED_FDS */
109 * Switch to a different file and seek to a position.
110 * @param slot Slot number of the new file.
111 * @param pos New absolute position in the new file.
113 void FioSeekToFile(uint8 slot
, size_t pos
)
116 #if defined(LIMITED_FDS)
117 /* Make sure we have this file open */
118 FioRestoreFile(slot
);
119 #endif /* LIMITED_FDS */
120 f
= _fio
.handles
[slot
];
123 _fio
.filename
= _fio
.filenames
[slot
];
124 FioSeekTo(pos
, SEEK_SET
);
128 * Read a byte from the file.
133 if (_fio
.buffer
== _fio
.buffer_end
) {
134 _fio
.buffer
= _fio
.buffer_start
;
135 size_t size
= fread(_fio
.buffer
, 1, FIO_BUFFER_SIZE
, _fio
.cur_fh
);
137 _fio
.buffer_end
= _fio
.buffer_start
+ size
;
139 if (size
== 0) return 0;
141 return *_fio
.buffer
++;
145 * Skip \a n bytes ahead in the file.
146 * @param n Number of bytes to skip reading.
148 void FioSkipBytes(int n
)
151 int m
= min(_fio
.buffer_end
- _fio
.buffer
, n
);
161 * Read a word (16 bits) from the file (in low endian format).
166 byte b
= FioReadByte();
167 return (FioReadByte() << 8) | b
;
171 * Read a double word (32 bits) from the file (in low endian format).
174 uint32
FioReadDword()
176 uint b
= FioReadWord();
177 return (FioReadWord() << 16) | b
;
182 * @param ptr Destination buffer.
183 * @param size Number of bytes to read.
185 void FioReadBlock(void *ptr
, size_t size
)
187 FioSeekTo(FioGetPos(), SEEK_SET
);
188 _fio
.pos
+= fread(ptr
, 1, size
, _fio
.cur_fh
);
192 * Close the file at the given slot number.
193 * @param slot File index to close.
195 static inline void FioCloseFile(int slot
)
197 if (_fio
.handles
[slot
] != NULL
) {
198 fclose(_fio
.handles
[slot
]);
200 free(_fio
.shortnames
[slot
]);
201 _fio
.shortnames
[slot
] = NULL
;
203 _fio
.handles
[slot
] = NULL
;
204 #if defined(LIMITED_FDS)
206 #endif /* LIMITED_FDS */
210 /** Close all slotted open files. */
213 for (int i
= 0; i
!= lengthof(_fio
.handles
); i
++) {
218 #if defined(LIMITED_FDS)
219 static void FioFreeHandle()
221 /* If we are about to open a file that will exceed the limit, close a file */
222 if (_fio
.open_handles
+ 1 == LIMITED_FDS
) {
228 /* Find the file that is used the least */
229 for (i
= 0; i
< lengthof(_fio
.handles
); i
++) {
230 if (_fio
.handles
[i
] != NULL
&& _fio
.usage_count
[i
] < count
) {
231 count
= _fio
.usage_count
[i
];
236 DEBUG(misc
, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio
.filenames
[slot
], slot
);
240 #endif /* LIMITED_FDS */
243 * Open a slotted file.
244 * @param slot Index to assign.
245 * @param filename Name of the file at the disk.
246 * @param subdir The sub directory to search this file in.
248 void FioOpenFile(int slot
, const char *filename
, Subdirectory subdir
)
252 #if defined(LIMITED_FDS)
254 #endif /* LIMITED_FDS */
255 f
= FioFOpenFile(filename
, "rb", subdir
);
256 if (f
== NULL
) usererror("Cannot open file '%s'", filename
);
258 if (pos
< 0) usererror("Cannot read file '%s'", filename
);
260 FioCloseFile(slot
); // if file was opened before, close it
261 _fio
.handles
[slot
] = f
;
262 _fio
.filenames
[slot
] = filename
;
264 /* Store the filename without path and extension */
265 const char *t
= strrchr(filename
, PATHSEPCHAR
);
266 _fio
.shortnames
[slot
] = xstrdup(t
== NULL
? filename
: t
);
267 char *t2
= strrchr(_fio
.shortnames
[slot
], '.');
268 if (t2
!= NULL
) *t2
= '\0';
269 strtolower(_fio
.shortnames
[slot
]);
271 #if defined(LIMITED_FDS)
272 _fio
.usage_count
[slot
] = 0;
274 #endif /* LIMITED_FDS */
275 FioSeekToFile(slot
, (uint32
)pos
);
278 static const char * const _subdirs
[] = {
281 "save" PATHSEP
"autosave" PATHSEP
,
283 "scenario" PATHSEP
"heightmap" PATHSEP
,
290 "ai" PATHSEP
"library" PATHSEP
,
292 "game" PATHSEP
"library" PATHSEP
,
293 "screenshot" PATHSEP
,
295 assert_compile(lengthof(_subdirs
) == NUM_SUBDIRS
);
297 const char *_searchpaths
[NUM_SEARCHPATHS
];
299 TarCache
TarCache::cache
[NUM_SUBDIRS
];
302 * Check whether the given file exists
303 * @param filename the file to try for existence.
304 * @param subdir the subdirectory to look in
305 * @return true if and only if the file can be opened
307 bool FioCheckFileExists(const char *filename
, Subdirectory subdir
)
309 FILE *f
= FioFOpenFile(filename
, "rb", subdir
);
310 if (f
== NULL
) return false;
317 * Test whether the given filename exists.
318 * @param filename the file to test.
319 * @return true if and only if the file exists.
321 bool FileExists(const char *filename
)
324 /* There is always one platform that doesn't support basic commands... */
325 HANDLE hand
= CreateFile(OTTD2FS(filename
), 0, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
326 if (hand
== INVALID_HANDLE_VALUE
) return 1;
330 return access(OTTD2FS(filename
), 0) == 0;
335 * Close a file in a safe way.
337 void FioFCloseFile(FILE *f
)
342 int FioGetFullPath (char *buf
, size_t buflen
, Searchpath sp
, Subdirectory subdir
, const char *filename
)
344 assert(subdir
< NUM_SUBDIRS
);
345 assert(sp
< NUM_SEARCHPATHS
);
347 return snprintf (buf
, buflen
, "%s%s%s", _searchpaths
[sp
], _subdirs
[subdir
], filename
!= NULL
? filename
: "");
351 * Find a path to the filename in one of the search directories.
352 * @param buf [out] Destination buffer for the path.
353 * @param buflen Length of the destination buffer.
354 * @param subdir Subdirectory to try.
355 * @param filename Filename to look for.
356 * @return \a buf containing the path if the path was found, else \c NULL.
358 char *FioFindFullPath(char *buf
, size_t buflen
, Subdirectory subdir
, const char *filename
)
361 assert(subdir
< NUM_SUBDIRS
);
363 FOR_ALL_SEARCHPATHS(sp
) {
364 FioGetFullPath(buf
, buflen
, sp
, subdir
, filename
);
365 if (FileExists(buf
)) return buf
;
367 /* Be, as opening files, aware that sometimes the filename
368 * might be in uppercase when it is in lowercase on the
369 * disk. Of course Windows doesn't care about casing. */
370 if (strtolower(buf
+ strlen(_searchpaths
[sp
]) - 1) && FileExists(buf
)) return buf
;
377 char *FioGetDirectory(char *buf
, size_t buflen
, Subdirectory subdir
)
381 /* Find and return the first valid directory */
382 FOR_ALL_SEARCHPATHS(sp
) {
383 FioGetFullPath (buf
, buflen
, sp
, subdir
);
384 if (FileExists(buf
)) return buf
;
387 /* Could not find the directory, fall back to a base path */
388 ttd_strlcpy(buf
, _personal_dir
, buflen
);
393 static FILE *FioFOpenFileSp(const char *filename
, const char *mode
, Searchpath sp
, Subdirectory subdir
, size_t *filesize
)
395 #if defined(WIN32) && defined(UNICODE)
396 /* fopen is implemented as a define with ellipses for
397 * Unicode support (prepend an L). As we are not sending
398 * a string, but a variable, it 'renames' the variable,
399 * so make that variable to makes it compile happily */
401 MultiByteToWideChar(CP_ACP
, 0, mode
, -1, Lmode
, lengthof(Lmode
));
406 if (subdir
== NO_DIRECTORY
) {
407 bstrcpy (buf
, filename
);
409 bstrfmt (buf
, "%s%s%s", _searchpaths
[sp
], _subdirs
[subdir
], filename
);
413 if (mode
[0] == 'r' && GetFileAttributes(OTTD2FS(buf
)) == INVALID_FILE_ATTRIBUTES
) return NULL
;
416 f
= fopen(buf
, mode
);
418 if (f
== NULL
&& strtolower(buf
+ ((subdir
== NO_DIRECTORY
) ? 0 : strlen(_searchpaths
[sp
]) - 1))) {
419 f
= fopen(buf
, mode
);
422 if (f
!= NULL
&& filesize
!= NULL
) {
423 /* Find the size of the file */
424 fseek(f
, 0, SEEK_END
);
425 *filesize
= ftell(f
);
426 fseek(f
, 0, SEEK_SET
);
432 * Opens a file from inside a tar archive.
433 * @param entry The entry to open.
434 * @param filesize [out] If not \c NULL, size of the opened file.
435 * @return File handle of the opened file, or \c NULL if the file is not available.
436 * @note The file is read from within the tar file, and may not return \c EOF after reading the whole file.
438 FILE *FioFOpenFileTar(TarFileListEntry
*entry
, size_t *filesize
)
440 FILE *f
= fopen(entry
->tar_filename
, "rb");
441 if (f
== NULL
) return f
;
443 if (fseek(f
, entry
->position
, SEEK_SET
) < 0) {
448 if (filesize
!= NULL
) *filesize
= entry
->size
;
453 * Opens a OpenTTD file somewhere in a personal or global directory.
454 * @param filename Name of the file to open.
455 * @param subdir Subdirectory to open.
456 * @param filename Name of the file to open.
457 * @return File handle of the opened file, or \c NULL if the file is not available.
459 FILE *FioFOpenFile(const char *filename
, const char *mode
, Subdirectory subdir
, size_t *filesize
)
464 assert(subdir
< NUM_SUBDIRS
|| subdir
== NO_DIRECTORY
);
466 FOR_ALL_SEARCHPATHS(sp
) {
467 f
= FioFOpenFileSp(filename
, mode
, sp
, subdir
, filesize
);
468 if (f
!= NULL
|| subdir
== NO_DIRECTORY
) break;
471 /* We can only use .tar in case of data-dir, and read-mode */
472 if (f
== NULL
&& mode
[0] == 'r' && subdir
!= NO_DIRECTORY
) {
473 static const uint MAX_RESOLVED_LENGTH
= 2 * (100 + 100 + 155) + 1; // Enough space to hold two filenames plus link. See 'TarHeader'.
474 sstring
<MAX_RESOLVED_LENGTH
> resolved_name
;
476 /* Filenames in tars are always forced to be lowercase */
477 resolved_name
.copy (filename
);
478 resolved_name
.tolower();
480 /* Resolve ONE directory link */
481 for (TarLinkList::iterator link
= TarCache::cache
[subdir
].links
.begin(); link
!= TarCache::cache
[subdir
].links
.end(); link
++) {
482 const std::string
&src
= link
->first
;
483 size_t len
= src
.length();
484 if (resolved_name
.length() >= len
&& resolved_name
.c_str()[len
- 1] == PATHSEPCHAR
&& memcmp (src
.c_str(), resolved_name
.c_str(), len
) == 0) {
486 char resolved_name2
[MAX_RESOLVED_LENGTH
];
487 bstrcpy (resolved_name2
, resolved_name
.c_str() + len
);
488 resolved_name
.copy (link
->second
.c_str());
489 resolved_name
.append (resolved_name2
);
490 break; // Only resolve one level
494 TarFileList::iterator it
= TarCache::cache
[subdir
].files
.find(resolved_name
.c_str());
495 if (it
!= TarCache::cache
[subdir
].files
.end()) {
496 f
= FioFOpenFileTar(&((*it
).second
), filesize
);
500 /* Sometimes a full path is given. To support
501 * the 'subdirectory' must be 'removed'. */
502 if (f
== NULL
&& subdir
!= NO_DIRECTORY
) {
505 f
= FioFOpenFile(filename
, mode
, OLD_GM_DIR
, filesize
);
506 if (f
!= NULL
) break;
509 f
= FioFOpenFile(filename
, mode
, OLD_DATA_DIR
, filesize
);
513 f
= FioFOpenFile(filename
, mode
, NO_DIRECTORY
, filesize
);
522 * Create a directory with the given name
523 * @param name the new name of the directory
525 static void FioCreateDirectory(const char *name
)
527 /* Ignore directory creation errors; they'll surface later on, and most
528 * of the time they are 'directory already exists' errors anyhow. */
529 #if defined(WIN32) || defined(WINCE)
530 CreateDirectory(OTTD2FS(name
), NULL
);
531 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
532 mkdir(OTTD2FS(name
));
533 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
537 size_t len
= strlen(name
) - 1;
538 if (buf
[len
] == '/') {
539 buf
[len
] = '\0'; // Kill pathsep, so mkdir() will not fail
542 mkdir(OTTD2FS(buf
), 0755);
544 mkdir(OTTD2FS(name
), 0755);
549 * Appends, if necessary, the path separator character to the end of the string.
550 * It does not add the path separator to zero-sized strings.
551 * @param buf string to append the separator to
552 * @return true iff the operation succeeded
554 static bool AppendPathSeparator (stringb
*buf
)
556 size_t s
= buf
->length();
558 return (s
== 0) || (buf
->get_buffer()[s
-1] == PATHSEPCHAR
)
559 || buf
->append (PATHSEPCHAR
);
563 * Construct a path by concatenating the given parts with intervening and
564 * trailing path separators, and return it in an newly allocated buffer.
565 * @param n the number of components to concatenate
566 * @param parts the strings to concatenate
567 * @return the resulting path, in a buffer allocated with malloc
569 char *BuildDirPath (uint n
, const char *const *parts
)
571 /* some sanity checks */
573 assert (parts
[0][0] != '\0'); // the first string must not be empty
576 /* change and recompile if you need something bigger */
577 assert (n
<= lengthof(lengths
));
579 size_t total
= n
+ 1; // account for path separators and trailing null
580 for (uint i
= 0; i
< n
; i
++) {
581 total
+= lengths
[i
] = strlen (parts
[i
]);
584 char *buf
= xmalloc (total
);
587 for (uint i
= 0; i
< n
; i
++) {
588 assert (p
+ lengths
[i
] + 1 < buf
+ total
);
589 memcpy (p
, parts
[i
], lengths
[i
]);
591 /* the first string is not empty, so p > buf */
592 if (p
[-1] != PATHSEPCHAR
) *p
++ = PATHSEPCHAR
;
600 void TarCache::add_link (const std::string
&srcp
, const std::string
&destp
)
602 std::string src
= srcp
;
603 std::string dest
= destp
;
604 /* Tar internals assume lowercase */
605 std::transform(src
.begin(), src
.end(), src
.begin(), tolower
);
606 std::transform(dest
.begin(), dest
.end(), dest
.begin(), tolower
);
608 TarFileList::iterator dest_file
= this->files
.find(dest
);
609 if (dest_file
!= this->files
.end()) {
610 /* Link to file. Process the link like the destination file. */
611 this->files
.insert (TarFileList::value_type (src
, dest_file
->second
));
613 /* Destination file not found. Assume 'link to directory'
614 * Append PATHSEPCHAR to 'src' and 'dest' if needed */
615 const std::string src_path
= ((*src
.rbegin() == PATHSEPCHAR
) ? src
: src
+ PATHSEPCHAR
);
616 const std::string dst_path
= (dest
.length() == 0 ? "" : ((*dest
.rbegin() == PATHSEPCHAR
) ? dest
: dest
+ PATHSEPCHAR
));
617 this->links
.insert (TarLinkList::value_type (src_path
, dst_path
));
622 * Simplify filenames from tars.
623 * Replace '/' by #PATHSEPCHAR, and force 'name' to lowercase.
624 * @param name Filename to process.
626 static void SimplifyFileName(char *name
)
628 /* Force lowercase */
631 /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
632 #if (PATHSEPCHAR != '/')
633 for (char *n
= name
; *n
!= '\0'; n
++) if (*n
== '/') *n
= PATHSEPCHAR
;
638 * Perform the scanning of a particular subdirectory.
639 * @param subdir The subdirectory to scan.
640 * @return The number of found tar files.
642 uint
TarScanner::DoScan(Subdirectory sd
)
644 TarCache::cache
[sd
].files
.clear();
645 TarCache::cache
[sd
].tars
.clear();
646 uint num
= this->Scan (sd
);
647 if (sd
== BASESET_DIR
|| sd
== NEWGRF_DIR
) num
+= this->Scan (OLD_DATA_DIR
);
651 /* static */ uint
TarScanner::DoScan(TarScanner::Mode mode
)
653 DEBUG(misc
, 1, "Scanning for tars");
656 if (mode
& TarScanner::BASESET
) {
657 num
+= fs
.DoScan(BASESET_DIR
);
659 if (mode
& TarScanner::NEWGRF
) {
660 num
+= fs
.DoScan(NEWGRF_DIR
);
662 if (mode
& TarScanner::AI
) {
663 num
+= fs
.DoScan(AI_DIR
);
664 num
+= fs
.DoScan(AI_LIBRARY_DIR
);
666 if (mode
& TarScanner::GAME
) {
667 num
+= fs
.DoScan(GAME_DIR
);
668 num
+= fs
.DoScan(GAME_LIBRARY_DIR
);
670 if (mode
& TarScanner::SCENARIO
) {
671 num
+= fs
.DoScan(SCENARIO_DIR
);
672 num
+= fs
.DoScan(HEIGHTMAP_DIR
);
674 DEBUG(misc
, 1, "Scan complete, found %d files", num
);
679 * Add a scanned file to the scanned files of a tar.
680 * @param filename the full path to the file to read
681 * @param basepath_length amount of characters to chop of before to get a
682 * filename relative to the search path.
683 * @param tar_filename the name of the tar file the file is read from.
684 * @return true if the file is added.
686 bool TarScanner::AddFile(const char *filename
, size_t basepath_length
, const char *tar_filename
)
688 /* No tar within tar. */
689 assert(tar_filename
== NULL
);
691 return TarCache::cache
[this->subdir
].add (filename
, basepath_length
);
695 * Add a scanned file to a tar cache.
696 * @param filename the full path to the file to read
697 * @param basepath_length amount of characters to chop of before to get a
698 * filename relative to the search path.
699 * @return true if the file is added.
701 bool TarCache::add (const char *filename
, size_t basepath_length
)
703 /* The TAR-header, repeated for every file */
705 char name
[100]; ///< Name of the file
709 char size
[12]; ///< Size of the file, in ASCII
720 char prefix
[155]; ///< Path of the file
725 assert_compile (sizeof(TarHeader
) == 512);
727 /* Check if we already seen this file */
728 TarList::iterator it
= this->tars
.find(filename
);
729 if (it
!= this->tars
.end()) return false;
731 FILE *f
= fopen(filename
, "rb");
732 /* Although the file has been found there can be
733 * a number of reasons we cannot open the file.
734 * Most common case is when we simply have not
735 * been given read access. */
736 if (f
== NULL
) return false;
738 char *dupped_filename
= xstrdup(filename
);
739 this->tars
[filename
].filename
.reset (dupped_filename
);
740 this->tars
[filename
].dirname
.reset();
742 TarLinkList links
; ///< Temporary list to collect links
743 size_t num
= 0, pos
= 0;
745 for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
747 if (fread (&th
, 1, 512, f
) != 512) break;
750 /* Check if we have the new tar-format (ustar). */
751 if (strncmp(th
.magic
, "ustar", 5) != 0) {
752 /* Check if we have the old one (header zeroed after 'link' field). */
753 if ((th
.magic
[0] != '\0') || (memcmp (&th
.magic
[0], &th
.magic
[1], 511 - offsetof(TarHeader
, magic
)) != 0)) {
754 DEBUG(misc
, 0, "The file '%s' isn't a valid tar-file", filename
);
760 /* The prefix contains the directory-name */
761 char name
[sizeof(th
.prefix
) + 1 + sizeof(th
.name
) + 1];
762 if (th
.prefix
[0] != '\0') {
763 bstrfmt (name
, "%.*s" PATHSEP
"%.*s",
764 (int)sizeof(th
.prefix
), th
.prefix
,
765 (int)sizeof(th
.name
), th
.name
);
767 bstrfmt (name
, "%.*s", (int)sizeof(th
.name
), th
.name
);
770 /* Calculate the size of the file.. for some strange reason this is stored as a string */
771 char buf
[sizeof(th
.size
) + 1];
772 bstrcpy (buf
, th
.size
);
773 size_t skip
= strtoul(buf
, NULL
, 8);
775 switch (th
.typeflag
) {
777 case '0': { // regular file
778 /* Ignore empty files */
779 if (skip
== 0) break;
781 if (strlen(name
) == 0) break;
783 /* Store this entry in the list */
784 TarFileListEntry entry
;
785 entry
.tar_filename
= dupped_filename
;
787 entry
.position
= pos
;
789 /* Convert to lowercase and our PATHSEPCHAR */
790 SimplifyFileName(name
);
792 DEBUG(misc
, 6, "Found file in tar: %s (" PRINTF_SIZE
" bytes, " PRINTF_SIZE
" offset)", name
, skip
, pos
);
793 if (this->files
.insert(TarFileList::value_type(name
, entry
)).second
) num
++;
798 case '1': // hard links
799 case '2': { // symbolic links
800 /* Copy the destination of the link in a safe way at the end of 'linkname' */
801 char link
[sizeof(th
.linkname
) + 1];
802 bstrcpy (link
, th
.linkname
);
804 if (strlen(name
) == 0 || strlen(link
) == 0) break;
806 /* Convert to lowercase and our PATHSEPCHAR */
807 SimplifyFileName(name
);
808 SimplifyFileName(link
);
810 /* Only allow relative links */
811 if (link
[0] == PATHSEPCHAR
) {
812 DEBUG(misc
, 1, "Ignoring absolute link in tar: %s -> %s", name
, link
);
816 /* Process relative path.
817 * Note: The destination of links must not contain any directory-links. */
818 char dest
[sizeof(name
) + 1 + sizeof(th
.linkname
) + 1];
819 bstrcpy (dest
, name
);
820 char *destpos
= strrchr(dest
, PATHSEPCHAR
);
821 if (destpos
== NULL
) destpos
= dest
;
824 const char *pos
= link
;
826 const char *next
= strchr (pos
, PATHSEPCHAR
);
827 if (next
== NULL
) next
= pos
+ strlen(pos
);
829 /* Skip '.' (current dir) */
830 if (next
!= pos
+ 1 || pos
[0] != '.') {
831 if (next
== pos
+ 2 && pos
[0] == '.' && pos
[1] == '.') {
833 if (dest
[0] == '\0') {
834 DEBUG(misc
, 1, "Ignoring link pointing outside of data directory: %s -> %s", name
, link
);
838 /* Truncate 'dest' after last PATHSEPCHAR.
839 * This assumes that the truncated part is a real directory and not a link. */
840 destpos
= strrchr(dest
, PATHSEPCHAR
);
841 if (destpos
== NULL
) destpos
= dest
;
843 /* Append at end of 'dest' */
844 if (destpos
!= dest
) *(destpos
++) = PATHSEPCHAR
;
845 uint len
= next
- pos
;
846 assert (destpos
+ len
< endof(dest
));
847 memcpy (destpos
, pos
, len
); // Safe as we do '\0'-termination ourselves
848 destpos
+= next
- pos
;
853 if (*next
== '\0') break;
858 /* Store links in temporary list */
859 DEBUG(misc
, 6, "Found link in tar: %s -> %s", name
, dest
);
860 links
.insert(TarLinkList::value_type(name
, dest
));
865 case '5': // directory
866 /* Convert to lowercase and our PATHSEPCHAR */
867 SimplifyFileName(name
);
869 /* Store the first directory name we detect */
870 DEBUG(misc
, 6, "Found dir in tar: %s", name
);
871 if (!this->tars
[filename
].dirname
) {
872 this->tars
[filename
].dirname
.reset (xstrdup(name
));
877 /* Ignore other types */
881 /* Skip to the next block.. */
882 skip
= Align(skip
, 512);
883 if (fseek(f
, skip
, SEEK_CUR
) < 0) {
884 DEBUG(misc
, 0, "The file '%s' can't be read as a valid tar-file", filename
);
891 DEBUG(misc
, 1, "Found tar '%s' with " PRINTF_SIZE
" new files", filename
, num
);
894 /* Resolve file links and store directory links.
895 * We restrict usage of links to two cases:
896 * 1) Links to directories:
897 * Both the source path and the destination path must NOT contain any further links.
898 * When resolving files at most one directory link is resolved.
900 * The destination path must NOT contain any links.
901 * The source path may contain one directory link.
903 for (TarLinkList::iterator link
= links
.begin(); link
!= links
.end(); link
++) {
904 this->add_link (link
->first
, link
->second
);
911 * Extract the tar with the given filename in the directory
912 * where the tar resides.
913 * @param tar_filename the name of the tar to extract.
914 * @return false on failure.
916 bool TarCache::extract (const char *tar_filename
)
918 TarList::iterator it
= this->tars
.find(tar_filename
);
919 /* We don't know the file. */
920 if (it
== this->tars
.end()) return false;
922 /* The file doesn't have a sub directory! */
923 if (!(*it
).second
.dirname
) return false;
925 const char *p
= strrchr (tar_filename
, PATHSEPCHAR
);
926 /* The file's path does not have a separator? */
927 if (p
== NULL
) return false;
928 size_t base_length
= p
- tar_filename
+ 1;
930 sstring
<MAX_PATH
> filename
;
931 filename
.fmt ("%.*s%s", (int)base_length
, tar_filename
, (*it
).second
.dirname
.get());
932 DEBUG (misc
, 8, "Extracting %s to directory %s", tar_filename
, filename
.c_str());
933 FioCreateDirectory (filename
.c_str());
935 for (TarFileList::iterator it2
= this->files
.begin(); it2
!= this->files
.end(); it2
++) {
936 if (strcmp((*it2
).second
.tar_filename
, tar_filename
) != 0) continue;
938 filename
.truncate (base_length
);
939 filename
.append ((*it2
).first
.c_str());
941 DEBUG(misc
, 9, " extracting %s", filename
.c_str());
943 /* First open the file in the .tar. */
945 FILE *in
= FioFOpenFileTar(&(*it2
).second
, &to_copy
);
947 DEBUG(misc
, 6, "Extracting %s failed; could not open %s", filename
.c_str(), tar_filename
);
951 /* Now open the 'output' file. */
952 FILE *out
= fopen (filename
.c_str(), "wb");
954 DEBUG(misc
, 6, "Extracting %s failed; could not open %s", filename
.c_str(), filename
.c_str());
959 /* Now read from the tar and write it into the file. */
962 for (; to_copy
!= 0; to_copy
-= read
) {
963 read
= fread(buffer
, 1, min(to_copy
, lengthof(buffer
)), in
);
964 if (read
<= 0 || fwrite(buffer
, 1, read
, out
) != read
) break;
967 /* Close everything up. */
972 DEBUG(misc
, 6, "Extracting %s failed; still %i bytes to copy", filename
.c_str(), (int)to_copy
);
977 DEBUG(misc
, 9, " extraction successful");
981 #if defined(WIN32) || defined(WINCE)
983 * Determine the base (personal dir and game data dir) paths
984 * @param exe the path from the current path to the executable
985 * @note defined in the OS related files (os2.cpp, win32.cpp, unix.cpp etc)
987 extern void DetermineBasePaths(const char *exe
);
988 #else /* defined(WIN32) || defined(WINCE) */
991 * Changes the working directory to the path of the give executable.
992 * For OSX application bundles '.app' is the required extension of the bundle,
993 * so when we crop the path to there, when can remove the name of the bundle
994 * in the same way we remove the name from the executable name.
995 * @param exe the path to the executable
997 static bool ChangeWorkingDirectoryToExecutable(const char *exe
)
1002 bool success
= false;
1004 char *app_bundle
= strchr(tmp
, '.');
1005 while (app_bundle
!= NULL
&& strncasecmp(app_bundle
, ".app", 4) != 0) app_bundle
= strchr(&app_bundle
[1], '.');
1007 if (app_bundle
!= NULL
) *app_bundle
= '\0';
1008 #endif /* WITH_COCOA */
1009 char *s
= strrchr(tmp
, PATHSEPCHAR
);
1012 #if defined(__DJGPP__)
1013 /* If we want to go to the root, we can't use cd C:, but we must use '/' */
1014 if (s
> tmp
&& *(s
- 1) == ':') chdir("/");
1016 if (chdir(tmp
) != 0) {
1017 DEBUG(misc
, 0, "Directory with the binary does not exist?");
1026 * Whether we should scan the working directory.
1027 * It should not be scanned if it's the root or
1028 * the home directory as in both cases a big data
1029 * directory can cause huge amounts of unrelated
1030 * files scanned. Furthermore there are nearly no
1031 * use cases for the home/root directory to have
1032 * OpenTTD directories.
1033 * @return true if it should be scanned.
1035 static bool DoScanWorkingDirectory()
1037 /* No working directory, so nothing to do. */
1038 if (_searchpaths
[SP_WORKING_DIR
] == NULL
) return false;
1040 /* Working directory is root, so do nothing. */
1041 if (strcmp(_searchpaths
[SP_WORKING_DIR
], PATHSEP
) == 0) return false;
1043 /* No personal/home directory, so the working directory won't be that. */
1044 if (_searchpaths
[SP_PERSONAL_DIR
] == NULL
) return true;
1046 sstring
<MAX_PATH
> tmp
;
1047 tmp
.fmt ("%s%s", _searchpaths
[SP_WORKING_DIR
], PERSONAL_DIR
);
1048 AppendPathSeparator (&tmp
);
1049 return strcmp (tmp
.c_str(), _searchpaths
[SP_PERSONAL_DIR
]) != 0;
1052 /** strdup the current working directory, with a trailing path separator. */
1053 static char *dupcwd (void)
1055 char tmp
[MAX_PATH
];
1056 if (getcwd (tmp
, MAX_PATH
) == NULL
) *tmp
= '\0';
1057 return BuildDirPath (tmp
);
1061 * Determine the base (personal dir and game data dir) paths
1062 * @param exe the path to the executable
1064 static void DetermineBasePaths(const char *exe
)
1066 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1067 const char *xdg_data_home
= xdgDataHome(NULL
);
1068 _searchpaths
[SP_PERSONAL_DIR_XDG
] = BuildDirPath (xdg_data_home
,
1069 PERSONAL_DIR
[0] == '.' ? &PERSONAL_DIR
[1] : PERSONAL_DIR
);
1070 free(xdg_data_home
);
1072 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
1073 _searchpaths
[SP_PERSONAL_DIR
] = NULL
;
1077 find_directory(B_USER_SETTINGS_DIRECTORY
, &path
);
1078 char *homedir
= xstrdup(path
.Path());
1080 /* getenv is highly unsafe; duplicate it as soon as possible,
1081 * or at least before something else touches the environment
1082 * variables in any way. It can also contain all kinds of
1083 * unvalidated data we rather not want internally. */
1084 char *homedir
= getenv("HOME");
1085 if (homedir
!= NULL
) {
1086 homedir
= xstrdup (homedir
);
1088 const struct passwd
*pw
= getpwuid(getuid());
1089 homedir
= (pw
== NULL
) ? NULL
: xstrdup(pw
->pw_dir
);
1093 if (homedir
!= NULL
) {
1094 ValidateString(homedir
);
1095 _searchpaths
[SP_PERSONAL_DIR
] = BuildDirPath (homedir
, PERSONAL_DIR
);
1098 _searchpaths
[SP_PERSONAL_DIR
] = NULL
;
1102 #if defined(WITH_SHARED_DIR)
1103 _searchpaths
[SP_SHARED_DIR
] = BuildDirPath (SHARED_DIR
);
1105 _searchpaths
[SP_SHARED_DIR
] = NULL
;
1108 #if defined(__MORPHOS__) || defined(__AMIGA__)
1109 _searchpaths
[SP_WORKING_DIR
] = NULL
;
1111 _searchpaths
[SP_WORKING_DIR
] = dupcwd();
1114 _do_scan_working_directory
= DoScanWorkingDirectory();
1116 /* Change the working directory to that one of the executable */
1117 if (ChangeWorkingDirectoryToExecutable(exe
)) {
1118 _searchpaths
[SP_BINARY_DIR
] = dupcwd();
1120 _searchpaths
[SP_BINARY_DIR
] = NULL
;
1123 if (_searchpaths
[SP_WORKING_DIR
] != NULL
) {
1124 /* Go back to the current working directory. */
1125 if (chdir(_searchpaths
[SP_WORKING_DIR
]) != 0) {
1126 DEBUG(misc
, 0, "Failed to return to working directory!");
1130 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
1131 _searchpaths
[SP_INSTALLATION_DIR
] = NULL
;
1133 _searchpaths
[SP_INSTALLATION_DIR
] = BuildDirPath (GLOBAL_DATA_DIR
);
1136 extern char *cocoaSetApplicationBundleDir();
1137 _searchpaths
[SP_APPLICATION_BUNDLE_DIR
] = cocoaSetApplicationBundleDir();
1139 _searchpaths
[SP_APPLICATION_BUNDLE_DIR
] = NULL
;
1142 #endif /* defined(WIN32) || defined(WINCE) */
1144 const char *_personal_dir
;
1147 * Acquire the base paths (personal dir and game data dir),
1148 * fill all other paths (save dir, autosave dir etc) and
1149 * make the save and scenario directories.
1150 * @param exe the path from the current path to the executable
1152 void DeterminePaths(const char *exe
)
1154 DetermineBasePaths(exe
);
1157 FOR_ALL_SEARCHPATHS(sp
) {
1158 if (sp
== SP_WORKING_DIR
&& !_do_scan_working_directory
) continue;
1159 DEBUG(misc
, 4, "%s added as search path", _searchpaths
[sp
]);
1162 char config_buffer
[MAX_PATH
];
1163 const char *config_dir
;
1164 if (_config_file
!= NULL
) {
1165 char *end
= strrchr(_config_file
, PATHSEPCHAR
);
1166 config_dir
= (end
== NULL
) ? "" : xstrmemdup (_config_file
, end
- _config_file
);
1168 if (FioFindFullPath (config_buffer
, lengthof(config_buffer
), BASE_DIR
, "openttd.cfg") != NULL
) {
1169 char *end
= strrchr (config_buffer
, PATHSEPCHAR
);
1170 if (end
!= NULL
) end
[1] = '\0';
1171 config_dir
= xstrdup (config_buffer
);
1173 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1174 /* No previous configuration file found. Use the configuration folder from XDG. */
1175 stringb
config_home (config_buffer
);
1176 const char *xdg_config_home
= xdgConfigHome (NULL
);
1177 config_home
.fmt ("%s" PATHSEP
"%s", xdg_config_home
,
1178 PERSONAL_DIR
[0] == '.' ? &PERSONAL_DIR
[1] : PERSONAL_DIR
);
1179 free (xdg_config_home
);
1181 AppendPathSeparator (&config_home
);
1183 config_dir
= config_buffer
;
1185 static const Searchpath new_openttd_cfg_order
[] = {
1186 SP_PERSONAL_DIR
, SP_BINARY_DIR
, SP_WORKING_DIR
, SP_SHARED_DIR
, SP_INSTALLATION_DIR
1189 for (uint i
= 0; ; i
++) {
1190 assert (i
< lengthof(new_openttd_cfg_order
));
1191 config_dir
= _searchpaths
[new_openttd_cfg_order
[i
]];
1192 if (config_dir
!= NULL
) break;
1196 _config_file
= str_fmt ("%sopenttd.cfg", config_dir
);
1199 DEBUG(misc
, 3, "%s found as config directory", config_dir
);
1201 _highscore_file
= str_fmt("%shs.dat", config_dir
);
1202 extern char *_hotkeys_file
;
1203 _hotkeys_file
= str_fmt("%shotkeys.cfg", config_dir
);
1204 extern char *_windows_file
;
1205 _windows_file
= str_fmt("%swindows.cfg", config_dir
);
1207 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1208 if (config_dir
== config_buffer
) {
1209 /* We are using the XDG configuration home for the config file,
1210 * then store the rest in the XDG data home folder. */
1211 _personal_dir
= _searchpaths
[SP_PERSONAL_DIR_XDG
];
1212 FioCreateDirectory(_personal_dir
);
1216 _personal_dir
= config_dir
;
1219 /* Make the necessary folders */
1220 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
1221 FioCreateDirectory(config_dir
);
1224 DEBUG(misc
, 3, "%s found as personal directory", _personal_dir
);
1226 static const Subdirectory default_subdirs
[] = {
1227 SAVE_DIR
, AUTOSAVE_DIR
, SCENARIO_DIR
, HEIGHTMAP_DIR
, BASESET_DIR
, NEWGRF_DIR
, AI_DIR
, AI_LIBRARY_DIR
, GAME_DIR
, GAME_LIBRARY_DIR
, SCREENSHOT_DIR
1230 for (uint i
= 0; i
< lengthof(default_subdirs
); i
++) {
1231 char *dir
= str_fmt("%s%s", _personal_dir
, _subdirs
[default_subdirs
[i
]]);
1232 FioCreateDirectory(dir
);
1236 /* If we have network we make a directory for the autodownloading of content */
1237 _searchpaths
[SP_AUTODOWNLOAD_DIR
] = str_fmt("%s%s", _personal_dir
, "content_download" PATHSEP
);
1238 #ifdef ENABLE_NETWORK
1239 FioCreateDirectory(_searchpaths
[SP_AUTODOWNLOAD_DIR
]);
1241 /* Create the directory for each of the types of content */
1242 const Subdirectory dirs
[] = { SCENARIO_DIR
, HEIGHTMAP_DIR
, BASESET_DIR
, NEWGRF_DIR
, AI_DIR
, AI_LIBRARY_DIR
, GAME_DIR
, GAME_LIBRARY_DIR
};
1243 for (uint i
= 0; i
< lengthof(dirs
); i
++) {
1244 char *tmp
= str_fmt("%s%s", _searchpaths
[SP_AUTODOWNLOAD_DIR
], _subdirs
[dirs
[i
]]);
1245 FioCreateDirectory(tmp
);
1249 extern char *_log_file
;
1250 _log_file
= str_fmt("%sopenttd.log", _personal_dir
);
1251 #else /* ENABLE_NETWORK */
1252 /* If we don't have networking, we don't need to make the directory. But
1253 * if it exists we keep it, otherwise remove it from the search paths. */
1254 if (!FileExists(_searchpaths
[SP_AUTODOWNLOAD_DIR
])) {
1255 free(_searchpaths
[SP_AUTODOWNLOAD_DIR
]);
1256 _searchpaths
[SP_AUTODOWNLOAD_DIR
] = NULL
;
1258 #endif /* ENABLE_NETWORK */
1262 * Sanitizes a filename, i.e. removes all illegal characters from it.
1263 * @param filename the "\0" terminated filename
1265 void SanitizeFilename(char *filename
)
1267 for (; *filename
!= '\0'; filename
++) {
1268 switch (*filename
) {
1269 /* The following characters are not allowed in filenames
1270 * on at least one of the supported operating systems: */
1271 case ':': case '\\': case '*': case '?': case '/':
1272 case '<': case '>': case '|': case '"':
1280 * Load a file into memory.
1281 * @param filename Name of the file to load.
1282 * @param lenp [out] Length of loaded data.
1283 * @param maxsize Maximum size to load.
1284 * @return Pointer to new memory containing the loaded data, or \c NULL if loading failed.
1285 * @note If \a maxsize less than the length of the file, loading fails.
1287 void *ReadFileToMem(const char *filename
, size_t *lenp
, size_t maxsize
)
1289 FILE *in
= fopen(filename
, "rb");
1290 if (in
== NULL
) return NULL
;
1292 fseek(in
, 0, SEEK_END
);
1293 size_t len
= ftell(in
);
1294 fseek(in
, 0, SEEK_SET
);
1295 if (len
> maxsize
) {
1299 byte
*mem
= xmalloct
<byte
>(len
+ 1);
1301 if (fread(mem
, len
, 1, in
) != 1) {
1313 * Helper to see whether a given filename matches the extension.
1314 * @param extension The extension to look for.
1315 * @param filename The filename to look in for the extension.
1316 * @return True iff the extension is NULL, or the filename ends with it.
1318 static bool MatchesExtension(const char *extension
, const char *filename
)
1320 if (extension
== NULL
) return true;
1322 const char *ext
= strrchr(filename
, extension
[0]);
1323 return ext
!= NULL
&& strcasecmp(ext
, extension
) == 0;
1327 * Scan a single directory (and recursively its children) and add
1328 * any graphics sets that are found.
1329 * @param fs the file scanner to add the files to
1330 * @param extension the extension of files to search for.
1331 * @param path full path we're currently at
1332 * @param basepath_length from where in the path are we 'based' on the search path
1333 * @param recursive whether to recursively search the sub directories
1335 static uint
ScanPath(FileScanner
*fs
, const char *extension
, const char *path
, size_t basepath_length
, bool recursive
)
1337 extern bool FiosIsValidFile(const char *path
, const struct dirent
*ent
, struct stat
*sb
);
1341 struct dirent
*dirent
;
1344 if (path
== NULL
|| (dir
= ttd_opendir(path
)) == NULL
) return 0;
1346 while ((dirent
= readdir(dir
)) != NULL
) {
1347 const char *d_name
= FS2OTTD(dirent
->d_name
);
1348 sstring
<MAX_PATH
> filename
;
1350 if (!FiosIsValidFile(path
, dirent
, &sb
)) continue;
1352 filename
.fmt ("%s%s", path
, d_name
);
1354 if (S_ISDIR(sb
.st_mode
)) {
1356 if (!recursive
) continue;
1357 if (strcmp(d_name
, ".") == 0 || strcmp(d_name
, "..") == 0) continue;
1358 if (!AppendPathSeparator (&filename
)) continue;
1359 num
+= ScanPath (fs
, extension
, filename
.c_str(), basepath_length
, recursive
);
1360 } else if (S_ISREG(sb
.st_mode
)) {
1362 if (MatchesExtension (extension
, filename
.c_str()) && fs
->AddFile (filename
.c_str(), basepath_length
, NULL
)) num
++;
1372 * Scan the given tar and add graphics sets when it finds one.
1373 * @param fs the file scanner to scan for
1374 * @param extension the extension of files to search for.
1375 * @param tar the tar to search in.
1377 static uint
ScanTar(FileScanner
*fs
, const char *extension
, TarFileList::iterator tar
)
1380 const char *filename
= (*tar
).first
.c_str();
1382 if (MatchesExtension(extension
, filename
) && fs
->AddFile(filename
, 0, (*tar
).second
.tar_filename
)) num
++;
1388 * Scan for files with the given extension in the given search path.
1389 * @param extension the extension of files to search for.
1390 * @param sd the sub directory to search in.
1391 * @param tars whether to search in the tars too.
1392 * @param recursive whether to search recursively
1393 * @return the number of found files, i.e. the number of times that
1394 * AddFile returned true.
1396 uint
FileScanner::Scan(const char *extension
, Subdirectory sd
, bool tars
, bool recursive
)
1401 char path
[MAX_PATH
];
1402 TarFileList::iterator tar
;
1405 FOR_ALL_SEARCHPATHS(sp
) {
1406 /* Don't search in the working directory */
1407 if (sp
== SP_WORKING_DIR
&& !_do_scan_working_directory
) continue;
1409 FioGetFullPath (path
, MAX_PATH
, sp
, sd
);
1410 num
+= ScanPath(this, extension
, path
, strlen(path
), recursive
);
1413 if (tars
&& sd
!= NO_DIRECTORY
) {
1414 FOR_ALL_TARS(tar
, sd
) {
1415 num
+= ScanTar(this, extension
, tar
);
1421 num
+= this->Scan(extension
, OLD_GM_DIR
, tars
, recursive
);
1424 num
+= this->Scan(extension
, OLD_DATA_DIR
, tars
, recursive
);
1434 * Scan for files with the given extension in the given search path.
1435 * @param extension the extension of files to search for.
1436 * @param directory the sub directory to search in.
1437 * @param dirend if not null, end directory here
1438 * @param recursive whether to search recursively
1439 * @return the number of found files, i.e. the number of times that
1440 * AddFile returned true.
1442 uint
FileScanner::Scan (const char *extension
, const char *directory
,
1443 const char *dirend
, bool recursive
)
1445 sstring
<MAX_PATH
> path
;
1446 if (dirend
== NULL
) {
1447 path
.copy (directory
);
1449 path
.fmt ("%.*s", (int)(dirend
- directory
), directory
);
1451 if (!AppendPathSeparator (&path
)) return 0;
1452 return ScanPath (this, extension
, path
.c_str(), path
.length(), recursive
);