00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "fileio_func.h"
00014 #include "debug.h"
00015 #include "fios.h"
00016 #include "string_func.h"
00017 #include "tar_type.h"
00018 #ifdef WIN32
00019 #include <windows.h>
00020 # define access _taccess
00021 #elif defined(__HAIKU__)
00022 #include <Path.h>
00023 #include <storage/FindDirectory.h>
00024 #else
00025 #include <unistd.h>
00026 #include <pwd.h>
00027 #endif
00028 #include <sys/stat.h>
00029 #include <algorithm>
00030
00031 #ifdef WITH_XDG_BASEDIR
00032 #include "basedir.h"
00033 #endif
00034
00036 #define FIO_BUFFER_SIZE 512
00037
00039 struct Fio {
00040 byte *buffer, *buffer_end;
00041 size_t pos;
00042 FILE *cur_fh;
00043 const char *filename;
00044 FILE *handles[MAX_FILE_SLOTS];
00045 byte buffer_start[FIO_BUFFER_SIZE];
00046 const char *filenames[MAX_FILE_SLOTS];
00047 char *shortnames[MAX_FILE_SLOTS];
00048 #if defined(LIMITED_FDS)
00049 uint open_handles;
00050 uint usage_count[MAX_FILE_SLOTS];
00051 #endif
00052 };
00053
00054 static Fio _fio;
00055
00057 static bool _do_scan_working_directory = true;
00058
00059 extern char *_config_file;
00060 extern char *_highscore_file;
00061
00066 size_t FioGetPos()
00067 {
00068 return _fio.pos + (_fio.buffer - _fio.buffer_end);
00069 }
00070
00076 const char *FioGetFilename(uint8 slot)
00077 {
00078 return _fio.shortnames[slot];
00079 }
00080
00086 void FioSeekTo(size_t pos, int mode)
00087 {
00088 if (mode == SEEK_CUR) pos += FioGetPos();
00089 _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
00090 _fio.pos = pos;
00091 if (fseek(_fio.cur_fh, _fio.pos, SEEK_SET) < 0) {
00092 DEBUG(misc, 0, "Seeking in %s failed", _fio.filename);
00093 }
00094 }
00095
00096 #if defined(LIMITED_FDS)
00097 static void FioRestoreFile(int slot)
00098 {
00099
00100 if (_fio.handles[slot] == NULL) {
00101 DEBUG(misc, 6, "Restoring file '%s' in slot '%d' from disk", _fio.filenames[slot], slot);
00102 FioOpenFile(slot, _fio.filenames[slot]);
00103 }
00104 _fio.usage_count[slot]++;
00105 }
00106 #endif
00107
00113 void FioSeekToFile(uint8 slot, size_t pos)
00114 {
00115 FILE *f;
00116 #if defined(LIMITED_FDS)
00117
00118 FioRestoreFile(slot);
00119 #endif
00120 f = _fio.handles[slot];
00121 assert(f != NULL);
00122 _fio.cur_fh = f;
00123 _fio.filename = _fio.filenames[slot];
00124 FioSeekTo(pos, SEEK_SET);
00125 }
00126
00131 byte FioReadByte()
00132 {
00133 if (_fio.buffer == _fio.buffer_end) {
00134 _fio.buffer = _fio.buffer_start;
00135 size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
00136 _fio.pos += size;
00137 _fio.buffer_end = _fio.buffer_start + size;
00138
00139 if (size == 0) return 0;
00140 }
00141 return *_fio.buffer++;
00142 }
00143
00148 void FioSkipBytes(int n)
00149 {
00150 for (;;) {
00151 int m = min(_fio.buffer_end - _fio.buffer, n);
00152 _fio.buffer += m;
00153 n -= m;
00154 if (n == 0) break;
00155 FioReadByte();
00156 n--;
00157 }
00158 }
00159
00164 uint16 FioReadWord()
00165 {
00166 byte b = FioReadByte();
00167 return (FioReadByte() << 8) | b;
00168 }
00169
00174 uint32 FioReadDword()
00175 {
00176 uint b = FioReadWord();
00177 return (FioReadWord() << 16) | b;
00178 }
00179
00185 void FioReadBlock(void *ptr, size_t size)
00186 {
00187 FioSeekTo(FioGetPos(), SEEK_SET);
00188 _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
00189 }
00190
00195 static inline void FioCloseFile(int slot)
00196 {
00197 if (_fio.handles[slot] != NULL) {
00198 fclose(_fio.handles[slot]);
00199
00200 free(_fio.shortnames[slot]);
00201 _fio.shortnames[slot] = NULL;
00202
00203 _fio.handles[slot] = NULL;
00204 #if defined(LIMITED_FDS)
00205 _fio.open_handles--;
00206 #endif
00207 }
00208 }
00209
00211 void FioCloseAll()
00212 {
00213 for (int i = 0; i != lengthof(_fio.handles); i++) {
00214 FioCloseFile(i);
00215 }
00216 }
00217
00218 #if defined(LIMITED_FDS)
00219 static void FioFreeHandle()
00220 {
00221
00222 if (_fio.open_handles + 1 == LIMITED_FDS) {
00223 uint i, count;
00224 int slot;
00225
00226 count = UINT_MAX;
00227 slot = -1;
00228
00229 for (i = 0; i < lengthof(_fio.handles); i++) {
00230 if (_fio.handles[i] != NULL && _fio.usage_count[i] < count) {
00231 count = _fio.usage_count[i];
00232 slot = i;
00233 }
00234 }
00235 assert(slot != -1);
00236 DEBUG(misc, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.filenames[slot], slot);
00237 FioCloseFile(slot);
00238 }
00239 }
00240 #endif
00241
00248 void FioOpenFile(int slot, const char *filename, Subdirectory subdir)
00249 {
00250 FILE *f;
00251
00252 #if defined(LIMITED_FDS)
00253 FioFreeHandle();
00254 #endif
00255 f = FioFOpenFile(filename, "rb", subdir);
00256 if (f == NULL) usererror("Cannot open file '%s'", filename);
00257 long pos = ftell(f);
00258 if (pos < 0) usererror("Cannot read file '%s'", filename);
00259
00260 FioCloseFile(slot);
00261 _fio.handles[slot] = f;
00262 _fio.filenames[slot] = filename;
00263
00264
00265 const char *t = strrchr(filename, PATHSEPCHAR);
00266 _fio.shortnames[slot] = strdup(t == NULL ? filename : t);
00267 char *t2 = strrchr(_fio.shortnames[slot], '.');
00268 if (t2 != NULL) *t2 = '\0';
00269 strtolower(_fio.shortnames[slot]);
00270
00271 #if defined(LIMITED_FDS)
00272 _fio.usage_count[slot] = 0;
00273 _fio.open_handles++;
00274 #endif
00275 FioSeekToFile(slot, (uint32)pos);
00276 }
00277
00278 static const char * const _subdirs[] = {
00279 "",
00280 "save" PATHSEP,
00281 "save" PATHSEP "autosave" PATHSEP,
00282 "scenario" PATHSEP,
00283 "scenario" PATHSEP "heightmap" PATHSEP,
00284 "gm" PATHSEP,
00285 "data" PATHSEP,
00286 "baseset" PATHSEP,
00287 "newgrf" PATHSEP,
00288 "lang" PATHSEP,
00289 "ai" PATHSEP,
00290 "ai" PATHSEP "library" PATHSEP,
00291 "game" PATHSEP,
00292 "game" PATHSEP "library" PATHSEP,
00293 "screenshot" PATHSEP,
00294 };
00295 assert_compile(lengthof(_subdirs) == NUM_SUBDIRS);
00296
00297 const char *_searchpaths[NUM_SEARCHPATHS];
00298 TarList _tar_list[NUM_SUBDIRS];
00299 TarFileList _tar_filelist[NUM_SUBDIRS];
00300
00301 typedef std::map<std::string, std::string> TarLinkList;
00302 static TarLinkList _tar_linklist[NUM_SUBDIRS];
00303
00310 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
00311 {
00312 FILE *f = FioFOpenFile(filename, "rb", subdir);
00313 if (f == NULL) return false;
00314
00315 FioFCloseFile(f);
00316 return true;
00317 }
00318
00324 bool FileExists(const char *filename)
00325 {
00326 #if defined(WINCE)
00327
00328 HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
00329 if (hand == INVALID_HANDLE_VALUE) return 1;
00330 CloseHandle(hand);
00331 return 0;
00332 #else
00333 return access(OTTD2FS(filename), 0) == 0;
00334 #endif
00335 }
00336
00340 void FioFCloseFile(FILE *f)
00341 {
00342 fclose(f);
00343 }
00344
00345 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
00346 {
00347 assert(subdir < NUM_SUBDIRS);
00348 assert(sp < NUM_SEARCHPATHS);
00349
00350 snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00351 return buf;
00352 }
00353
00362 char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
00363 {
00364 Searchpath sp;
00365 assert(subdir < NUM_SUBDIRS);
00366
00367 FOR_ALL_SEARCHPATHS(sp) {
00368 FioGetFullPath(buf, buflen, sp, subdir, filename);
00369 if (FileExists(buf)) return buf;
00370 #if !defined(WIN32)
00371
00372
00373
00374 if (strtolower(buf + strlen(_searchpaths[sp]) - 1) && FileExists(buf)) return buf;
00375 #endif
00376 }
00377
00378 return NULL;
00379 }
00380
00381 char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
00382 {
00383 assert(subdir < NUM_SUBDIRS);
00384 assert(sp < NUM_SEARCHPATHS);
00385
00386 snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
00387 return buf;
00388 }
00389
00390 char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
00391 {
00392 Searchpath sp;
00393
00394
00395 FOR_ALL_SEARCHPATHS(sp) {
00396 char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
00397 if (FileExists(buf)) return ret;
00398 }
00399
00400
00401 ttd_strlcpy(buf, _personal_dir, buflen);
00402
00403 return buf;
00404 }
00405
00406 static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
00407 {
00408 #if defined(WIN32) && defined(UNICODE)
00409
00410
00411
00412
00413 wchar_t Lmode[5];
00414 MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
00415 #endif
00416 FILE *f = NULL;
00417 char buf[MAX_PATH];
00418
00419 if (subdir == NO_DIRECTORY) {
00420 strecpy(buf, filename, lastof(buf));
00421 } else {
00422 snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00423 }
00424
00425 #if defined(WIN32)
00426 if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
00427 #endif
00428
00429 f = fopen(buf, mode);
00430 #if !defined(WIN32)
00431 if (f == NULL && strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1))) {
00432 f = fopen(buf, mode);
00433 }
00434 #endif
00435 if (f != NULL && filesize != NULL) {
00436
00437 fseek(f, 0, SEEK_END);
00438 *filesize = ftell(f);
00439 fseek(f, 0, SEEK_SET);
00440 }
00441 return f;
00442 }
00443
00451 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
00452 {
00453 FILE *f = fopen(entry->tar_filename, "rb");
00454 if (f == NULL) return f;
00455
00456 if (fseek(f, entry->position, SEEK_SET) < 0) {
00457 fclose(f);
00458 return NULL;
00459 }
00460
00461 if (filesize != NULL) *filesize = entry->size;
00462 return f;
00463 }
00464
00472 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
00473 {
00474 FILE *f = NULL;
00475 Searchpath sp;
00476
00477 assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
00478
00479 FOR_ALL_SEARCHPATHS(sp) {
00480 f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
00481 if (f != NULL || subdir == NO_DIRECTORY) break;
00482 }
00483
00484
00485 if (f == NULL && mode[0] == 'r' && subdir != NO_DIRECTORY) {
00486 static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1;
00487 char resolved_name[MAX_RESOLVED_LENGTH];
00488
00489
00490 strecpy(resolved_name, filename, lastof(resolved_name));
00491 strtolower(resolved_name);
00492
00493 size_t resolved_len = strlen(resolved_name);
00494
00495
00496 for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
00497 const std::string &src = link->first;
00498 size_t len = src.length();
00499 if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
00500
00501 char resolved_name2[MAX_RESOLVED_LENGTH];
00502 const std::string &dest = link->second;
00503 strecpy(resolved_name2, &(resolved_name[len]), lastof(resolved_name2));
00504 strecpy(resolved_name, dest.c_str(), lastof(resolved_name));
00505 strecpy(&(resolved_name[dest.length()]), resolved_name2, lastof(resolved_name));
00506 break;
00507 }
00508 }
00509
00510 TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
00511 if (it != _tar_filelist[subdir].end()) {
00512 f = FioFOpenFileTar(&((*it).second), filesize);
00513 }
00514 }
00515
00516
00517
00518 if (f == NULL && subdir != NO_DIRECTORY) {
00519 switch (subdir) {
00520 case BASESET_DIR:
00521 f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
00522 if (f != NULL) break;
00523
00524 case NEWGRF_DIR:
00525 f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
00526 break;
00527
00528 default:
00529 f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
00530 break;
00531 }
00532 }
00533
00534 return f;
00535 }
00536
00541 static void FioCreateDirectory(const char *name)
00542 {
00543
00544
00545 #if defined(WIN32) || defined(WINCE)
00546 CreateDirectory(OTTD2FS(name), NULL);
00547 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
00548 mkdir(OTTD2FS(name));
00549 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
00550 char buf[MAX_PATH];
00551 ttd_strlcpy(buf, name, MAX_PATH);
00552
00553 size_t len = strlen(name) - 1;
00554 if (buf[len] == '/') {
00555 buf[len] = '\0';
00556 }
00557
00558 mkdir(OTTD2FS(buf), 0755);
00559 #else
00560 mkdir(OTTD2FS(name), 0755);
00561 #endif
00562 }
00563
00571 bool AppendPathSeparator(char *buf, size_t buflen)
00572 {
00573 size_t s = strlen(buf);
00574
00575
00576 if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
00577 if (s + 2 >= buflen) return false;
00578
00579 buf[s] = PATHSEPCHAR;
00580 buf[s + 1] = '\0';
00581 }
00582
00583 return true;
00584 }
00585
00592 char *BuildWithFullPath(const char *dir)
00593 {
00594 char *dest = MallocT<char>(MAX_PATH);
00595 ttd_strlcpy(dest, dir, MAX_PATH);
00596
00597
00598 const char *s = strchr(dest, PATHSEPCHAR);
00599
00600
00601 if (s == NULL || dest != s) {
00602 if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0';
00603 AppendPathSeparator(dest, MAX_PATH);
00604 ttd_strlcat(dest, dir, MAX_PATH);
00605 }
00606 AppendPathSeparator(dest, MAX_PATH);
00607
00608 return dest;
00609 }
00610
00616 const char *FioTarFirstDir(const char *tarname, Subdirectory subdir)
00617 {
00618 TarList::iterator it = _tar_list[subdir].find(tarname);
00619 if (it == _tar_list[subdir].end()) return NULL;
00620 return (*it).second.dirname;
00621 }
00622
00623 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
00624 {
00625 std::string src = srcParam;
00626 std::string dest = destParam;
00627
00628 std::transform(src.begin(), src.end(), src.begin(), tolower);
00629 std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
00630
00631 TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
00632 if (dest_file != _tar_filelist[subdir].end()) {
00633
00634 _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
00635 } else {
00636
00637
00638 const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
00639 const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
00640 _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
00641 }
00642 }
00643
00644 void FioTarAddLink(const char *src, const char *dest, Subdirectory subdir)
00645 {
00646 TarAddLink(src, dest, subdir);
00647 }
00648
00654 static void SimplifyFileName(char *name)
00655 {
00656
00657 strtolower(name);
00658
00659
00660 #if (PATHSEPCHAR != '/')
00661 for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
00662 #endif
00663 }
00664
00670 uint TarScanner::DoScan(Subdirectory sd)
00671 {
00672 _tar_filelist[sd].clear();
00673 _tar_list[sd].clear();
00674 uint num = this->Scan(".tar", sd, false);
00675 if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
00676 return num;
00677 }
00678
00679 uint TarScanner::DoScan(TarScanner::Mode mode)
00680 {
00681 DEBUG(misc, 1, "Scanning for tars");
00682 TarScanner fs;
00683 uint num = 0;
00684 if (mode & TarScanner::BASESET) {
00685 num += fs.DoScan(BASESET_DIR);
00686 }
00687 if (mode & TarScanner::NEWGRF) {
00688 num += fs.DoScan(NEWGRF_DIR);
00689 }
00690 if (mode & TarScanner::AI) {
00691 num += fs.DoScan(AI_DIR);
00692 num += fs.DoScan(AI_LIBRARY_DIR);
00693 }
00694 if (mode & TarScanner::GAME) {
00695 num += fs.DoScan(GAME_DIR);
00696 num += fs.DoScan(GAME_LIBRARY_DIR);
00697 }
00698 if (mode & TarScanner::SCENARIO) {
00699 num += fs.DoScan(SCENARIO_DIR);
00700 num += fs.DoScan(HEIGHTMAP_DIR);
00701 }
00702 DEBUG(misc, 1, "Scan complete, found %d files", num);
00703 return num;
00704 }
00705
00712 bool TarScanner::AddFile(Subdirectory sd, const char *filename)
00713 {
00714 this->subdir = sd;
00715 return this->AddFile(filename, 0);
00716 }
00717
00718 bool TarScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00719 {
00720
00721 assert(tar_filename == NULL);
00722
00723
00724 struct TarHeader {
00725 char name[100];
00726 char mode[8];
00727 char uid[8];
00728 char gid[8];
00729 char size[12];
00730 char mtime[12];
00731 char chksum[8];
00732 char typeflag;
00733 char linkname[100];
00734 char magic[6];
00735 char version[2];
00736 char uname[32];
00737 char gname[32];
00738 char devmajor[8];
00739 char devminor[8];
00740 char prefix[155];
00741
00742 char unused[12];
00743 };
00744
00745
00746 TarList::iterator it = _tar_list[this->subdir].find(filename);
00747 if (it != _tar_list[this->subdir].end()) return false;
00748
00749 FILE *f = fopen(filename, "rb");
00750
00751
00752
00753
00754 if (f == NULL) return false;
00755
00756 const char *dupped_filename = strdup(filename);
00757 _tar_list[this->subdir][filename].filename = dupped_filename;
00758 _tar_list[this->subdir][filename].dirname = NULL;
00759
00760 TarLinkList links;
00761
00762 TarHeader th;
00763 char buf[sizeof(th.name) + 1], *end;
00764 char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
00765 char link[sizeof(th.linkname) + 1];
00766 char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
00767 size_t num = 0, pos = 0;
00768
00769
00770 char empty[512];
00771 memset(&empty[0], 0, sizeof(empty));
00772
00773 for (;;) {
00774 size_t num_bytes_read = fread(&th, 1, 512, f);
00775 if (num_bytes_read != 512) break;
00776 pos += num_bytes_read;
00777
00778
00779 if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
00780
00781 if (memcmp(&th, &empty[0], 512) == 0) continue;
00782
00783 DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
00784 fclose(f);
00785 return false;
00786 }
00787
00788 name[0] = '\0';
00789
00790
00791 if (th.prefix[0] != '\0') {
00792 ttd_strlcpy(name, th.prefix, lengthof(name));
00793 ttd_strlcat(name, PATHSEP, lengthof(name));
00794 }
00795
00796
00797 ttd_strlcat(name, th.name, lengthof(name));
00798
00799
00800 ttd_strlcpy(buf, th.size, lengthof(buf));
00801 size_t skip = strtoul(buf, &end, 8);
00802
00803 switch (th.typeflag) {
00804 case '\0':
00805 case '0': {
00806
00807 if (skip == 0) break;
00808
00809 if (strlen(name) == 0) break;
00810
00811
00812 TarFileListEntry entry;
00813 entry.tar_filename = dupped_filename;
00814 entry.size = skip;
00815 entry.position = pos;
00816
00817
00818 SimplifyFileName(name);
00819
00820 DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
00821 if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
00822
00823 break;
00824 }
00825
00826 case '1':
00827 case '2': {
00828
00829 ttd_strlcpy(link, th.linkname, lengthof(link));
00830
00831 if (strlen(name) == 0 || strlen(link) == 0) break;
00832
00833
00834 SimplifyFileName(name);
00835 SimplifyFileName(link);
00836
00837
00838 if (link[0] == PATHSEPCHAR) {
00839 DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
00840 break;
00841 }
00842
00843
00844
00845 ttd_strlcpy(dest, name, lengthof(dest));
00846 char *destpos = strrchr(dest, PATHSEPCHAR);
00847 if (destpos == NULL) destpos = dest;
00848 *destpos = '\0';
00849
00850 char *pos = link;
00851 while (*pos != '\0') {
00852 char *next = strchr(link, PATHSEPCHAR);
00853 if (next == NULL) next = pos + strlen(pos);
00854
00855
00856 if (next != pos + 1 || pos[0] != '.') {
00857 if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') {
00858
00859 if (dest[0] == '\0') {
00860 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
00861 break;
00862 }
00863
00864
00865
00866 destpos = strrchr(dest, PATHSEPCHAR);
00867 if (destpos == NULL) destpos = dest;
00868 } else {
00869
00870 if (destpos != dest) *(destpos++) = PATHSEPCHAR;
00871 strncpy(destpos, pos, next - pos);
00872 destpos += next - pos;
00873 }
00874 *destpos = '\0';
00875 }
00876
00877 pos = next;
00878 }
00879
00880
00881 DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
00882 links.insert(TarLinkList::value_type(name, dest));
00883
00884 break;
00885 }
00886
00887 case '5':
00888
00889 SimplifyFileName(name);
00890
00891
00892 DEBUG(misc, 6, "Found dir in tar: %s", name);
00893 if (_tar_list[this->subdir][filename].dirname == NULL) _tar_list[this->subdir][filename].dirname = strdup(name);
00894 break;
00895
00896 default:
00897
00898 break;
00899 }
00900
00901
00902 skip = Align(skip, 512);
00903 if (fseek(f, skip, SEEK_CUR) < 0) {
00904 DEBUG(misc, 0, "The file '%s' can't be read as a valid tar-file", filename);
00905 fclose(f);
00906 return false;
00907 }
00908 pos += skip;
00909 }
00910
00911 DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
00912 fclose(f);
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
00924 const std::string &src = link->first;
00925 const std::string &dest = link->second;
00926 TarAddLink(src, dest, this->subdir);
00927 }
00928
00929 return true;
00930 }
00931
00939 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
00940 {
00941 TarList::iterator it = _tar_list[subdir].find(tar_filename);
00942
00943 if (it == _tar_list[subdir].end()) return false;
00944
00945 const char *dirname = (*it).second.dirname;
00946
00947
00948 if (dirname == NULL) return false;
00949
00950 char filename[MAX_PATH];
00951 strecpy(filename, tar_filename, lastof(filename));
00952 char *p = strrchr(filename, PATHSEPCHAR);
00953
00954 if (p == NULL) return false;
00955
00956 p++;
00957 strecpy(p, dirname, lastof(filename));
00958 DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
00959 FioCreateDirectory(filename);
00960
00961 for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
00962 if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
00963
00964 strecpy(p, (*it2).first.c_str(), lastof(filename));
00965
00966 DEBUG(misc, 9, " extracting %s", filename);
00967
00968
00969 size_t to_copy = 0;
00970 FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
00971 if (in == NULL) {
00972 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
00973 return false;
00974 }
00975
00976
00977 FILE *out = fopen(filename, "wb");
00978 if (out == NULL) {
00979 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
00980 fclose(in);
00981 return false;
00982 }
00983
00984
00985 char buffer[4096];
00986 size_t read;
00987 for (; to_copy != 0; to_copy -= read) {
00988 read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
00989 if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
00990 }
00991
00992
00993 fclose(in);
00994 fclose(out);
00995
00996 if (to_copy != 0) {
00997 DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
00998 return false;
00999 }
01000 }
01001
01002 DEBUG(misc, 9, " extraction successful");
01003 return true;
01004 }
01005
01006 #if defined(WIN32) || defined(WINCE)
01007
01012 extern void DetermineBasePaths(const char *exe);
01013 #else
01014
01022 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
01023 {
01024 bool success = false;
01025 #ifdef WITH_COCOA
01026 char *app_bundle = strchr(exe, '.');
01027 while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
01028
01029 if (app_bundle != NULL) app_bundle[0] = '\0';
01030 #endif
01031 char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
01032 if (s != NULL) {
01033 *s = '\0';
01034 #if defined(__DJGPP__)
01035
01036 if (s[-1] == ':') chdir("/");
01037 #endif
01038 if (chdir(exe) != 0) {
01039 DEBUG(misc, 0, "Directory with the binary does not exist?");
01040 } else {
01041 success = true;
01042 }
01043 *s = PATHSEPCHAR;
01044 }
01045 #ifdef WITH_COCOA
01046 if (app_bundle != NULL) app_bundle[0] = '.';
01047 #endif
01048 return success;
01049 }
01050
01061 bool DoScanWorkingDirectory()
01062 {
01063
01064 if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
01065
01066
01067 if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
01068
01069
01070 if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
01071
01072 char tmp[MAX_PATH];
01073 snprintf(tmp, lengthof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
01074 AppendPathSeparator(tmp, MAX_PATH);
01075 return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
01076 }
01077
01082 void DetermineBasePaths(const char *exe)
01083 {
01084 char tmp[MAX_PATH];
01085 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
01086 const char *xdg_data_home = xdgDataHome(NULL);
01087 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", xdg_data_home,
01088 PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
01089 free(xdg_data_home);
01090
01091 AppendPathSeparator(tmp, MAX_PATH);
01092 _searchpaths[SP_PERSONAL_DIR_XDG] = strdup(tmp);
01093 #endif
01094 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
01095 _searchpaths[SP_PERSONAL_DIR] = NULL;
01096 #else
01097 #ifdef __HAIKU__
01098 BPath path;
01099 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
01100 const char *homedir = strdup(path.Path());
01101 #else
01102
01103
01104
01105
01106 const char *homedir = getenv("HOME");
01107 if (homedir != NULL) {
01108 homedir = strndup(homedir, MAX_PATH);
01109 }
01110
01111 if (homedir == NULL) {
01112 const struct passwd *pw = getpwuid(getuid());
01113 homedir = (pw == NULL) ? NULL : strdup(pw->pw_dir);
01114 }
01115 #endif
01116
01117 if (homedir != NULL) {
01118 ValidateString(homedir);
01119 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
01120 AppendPathSeparator(tmp, MAX_PATH);
01121
01122 _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
01123 free(homedir);
01124 } else {
01125 _searchpaths[SP_PERSONAL_DIR] = NULL;
01126 }
01127 #endif
01128
01129 #if defined(WITH_SHARED_DIR)
01130 snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
01131 AppendPathSeparator(tmp, MAX_PATH);
01132 _searchpaths[SP_SHARED_DIR] = strdup(tmp);
01133 #else
01134 _searchpaths[SP_SHARED_DIR] = NULL;
01135 #endif
01136
01137 #if defined(__MORPHOS__) || defined(__AMIGA__)
01138 _searchpaths[SP_WORKING_DIR] = NULL;
01139 #else
01140 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01141 AppendPathSeparator(tmp, MAX_PATH);
01142 _searchpaths[SP_WORKING_DIR] = strdup(tmp);
01143 #endif
01144
01145 _do_scan_working_directory = DoScanWorkingDirectory();
01146
01147
01148 if (ChangeWorkingDirectoryToExecutable(exe)) {
01149 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01150 AppendPathSeparator(tmp, MAX_PATH);
01151 _searchpaths[SP_BINARY_DIR] = strdup(tmp);
01152 } else {
01153 _searchpaths[SP_BINARY_DIR] = NULL;
01154 }
01155
01156 if (_searchpaths[SP_WORKING_DIR] != NULL) {
01157
01158 if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
01159 DEBUG(misc, 0, "Failed to return to working directory!");
01160 }
01161 }
01162
01163 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
01164 _searchpaths[SP_INSTALLATION_DIR] = NULL;
01165 #else
01166 snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
01167 AppendPathSeparator(tmp, MAX_PATH);
01168 _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
01169 #endif
01170 #ifdef WITH_COCOA
01171 extern void cocoaSetApplicationBundleDir();
01172 cocoaSetApplicationBundleDir();
01173 #else
01174 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
01175 #endif
01176 }
01177 #endif
01178
01179 const char *_personal_dir;
01180
01187 void DeterminePaths(const char *exe)
01188 {
01189 DetermineBasePaths(exe);
01190
01191 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
01192 char config_home[MAX_PATH];
01193
01194 const char *xdg_config_home = xdgConfigHome(NULL);
01195 snprintf(config_home, MAX_PATH, "%s" PATHSEP "%s", xdg_config_home,
01196 PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
01197 free(xdg_config_home);
01198
01199 AppendPathSeparator(config_home, MAX_PATH);
01200 #endif
01201
01202 Searchpath sp;
01203 FOR_ALL_SEARCHPATHS(sp) {
01204 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01205 DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
01206 }
01207
01208 char *config_dir;
01209 if (_config_file != NULL) {
01210 config_dir = strdup(_config_file);
01211 char *end = strrchr(config_dir, PATHSEPCHAR);
01212 if (end == NULL) {
01213 config_dir[0] = '\0';
01214 } else {
01215 end[1] = '\0';
01216 }
01217 } else {
01218 char personal_dir[MAX_PATH];
01219 if (FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg") != NULL) {
01220 char *end = strrchr(personal_dir, PATHSEPCHAR);
01221 if (end != NULL) end[1] = '\0';
01222 config_dir = strdup(personal_dir);
01223 _config_file = str_fmt("%sopenttd.cfg", config_dir);
01224 } else {
01225 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
01226
01227 config_dir = config_home;
01228 #else
01229 static const Searchpath new_openttd_cfg_order[] = {
01230 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
01231 };
01232
01233 config_dir = NULL;
01234 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
01235 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
01236 config_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
01237 break;
01238 }
01239 }
01240 assert(config_dir != NULL);
01241 #endif
01242 _config_file = str_fmt("%sopenttd.cfg", config_dir);
01243 }
01244 }
01245
01246 DEBUG(misc, 3, "%s found as config directory", config_dir);
01247
01248 _highscore_file = str_fmt("%shs.dat", config_dir);
01249 extern char *_hotkeys_file;
01250 _hotkeys_file = str_fmt("%shotkeys.cfg", config_dir);
01251 extern char *_windows_file;
01252 _windows_file = str_fmt("%swindows.cfg", config_dir);
01253
01254 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
01255 if (config_dir == config_home) {
01256
01257
01258 _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
01259 FioCreateDirectory(_personal_dir);
01260 } else
01261 #endif
01262 {
01263 _personal_dir = config_dir;
01264 }
01265
01266
01267 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
01268 FioCreateDirectory(config_dir);
01269 if (config_dir != _personal_dir) FioCreateDirectory(_personal_dir);
01270 #endif
01271
01272 DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
01273
01274 static const Subdirectory default_subdirs[] = {
01275 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR
01276 };
01277
01278 for (uint i = 0; i < lengthof(default_subdirs); i++) {
01279 char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
01280 FioCreateDirectory(dir);
01281 free(dir);
01282 }
01283
01284
01285 _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
01286 #ifdef ENABLE_NETWORK
01287 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01288
01289
01290 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };
01291 for (uint i = 0; i < lengthof(dirs); i++) {
01292 char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
01293 FioCreateDirectory(tmp);
01294 free(tmp);
01295 }
01296
01297 extern char *_log_file;
01298 _log_file = str_fmt("%sopenttd.log", _personal_dir);
01299 #else
01300
01301
01302 if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
01303 free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01304 _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
01305 }
01306 #endif
01307 }
01308
01313 void SanitizeFilename(char *filename)
01314 {
01315 for (; *filename != '\0'; filename++) {
01316 switch (*filename) {
01317
01318
01319 case ':': case '\\': case '*': case '?': case '/':
01320 case '<': case '>': case '|': case '"':
01321 *filename = '_';
01322 break;
01323 }
01324 }
01325 }
01326
01335 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
01336 {
01337 FILE *in = fopen(filename, "rb");
01338 if (in == NULL) return NULL;
01339
01340 fseek(in, 0, SEEK_END);
01341 size_t len = ftell(in);
01342 fseek(in, 0, SEEK_SET);
01343 if (len > maxsize) {
01344 fclose(in);
01345 return NULL;
01346 }
01347 byte *mem = MallocT<byte>(len + 1);
01348 mem[len] = 0;
01349 if (fread(mem, len, 1, in) != 1) {
01350 fclose(in);
01351 free(mem);
01352 return NULL;
01353 }
01354 fclose(in);
01355
01356 *lenp = len;
01357 return mem;
01358 }
01359
01366 static bool MatchesExtension(const char *extension, const char *filename)
01367 {
01368 if (extension == NULL) return true;
01369
01370 const char *ext = strrchr(filename, extension[0]);
01371 return ext != NULL && strcasecmp(ext, extension) == 0;
01372 }
01373
01383 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
01384 {
01385 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
01386
01387 uint num = 0;
01388 struct stat sb;
01389 struct dirent *dirent;
01390 DIR *dir;
01391
01392 if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
01393
01394 while ((dirent = readdir(dir)) != NULL) {
01395 const char *d_name = FS2OTTD(dirent->d_name);
01396 char filename[MAX_PATH];
01397
01398 if (!FiosIsValidFile(path, dirent, &sb)) continue;
01399
01400 snprintf(filename, lengthof(filename), "%s%s", path, d_name);
01401
01402 if (S_ISDIR(sb.st_mode)) {
01403
01404 if (!recursive) continue;
01405 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01406 if (!AppendPathSeparator(filename, lengthof(filename))) continue;
01407 num += ScanPath(fs, extension, filename, basepath_length, recursive);
01408 } else if (S_ISREG(sb.st_mode)) {
01409
01410 if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, NULL)) num++;
01411 }
01412 }
01413
01414 closedir(dir);
01415
01416 return num;
01417 }
01418
01425 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01426 {
01427 uint num = 0;
01428 const char *filename = (*tar).first.c_str();
01429
01430 if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
01431
01432 return num;
01433 }
01434
01444 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
01445 {
01446 this->subdir = sd;
01447
01448 Searchpath sp;
01449 char path[MAX_PATH];
01450 TarFileList::iterator tar;
01451 uint num = 0;
01452
01453 FOR_ALL_SEARCHPATHS(sp) {
01454
01455 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01456
01457 FioAppendDirectory(path, MAX_PATH, sp, sd);
01458 num += ScanPath(this, extension, path, strlen(path), recursive);
01459 }
01460
01461 if (tars && sd != NO_DIRECTORY) {
01462 FOR_ALL_TARS(tar, sd) {
01463 num += ScanTar(this, extension, tar);
01464 }
01465 }
01466
01467 switch (sd) {
01468 case BASESET_DIR:
01469 num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
01470
01471 case NEWGRF_DIR:
01472 num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
01473 break;
01474
01475 default: break;
01476 }
01477
01478 return num;
01479 }
01480
01489 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
01490 {
01491 char path[MAX_PATH];
01492 strecpy(path, directory, lastof(path));
01493 if (!AppendPathSeparator(path, lengthof(path))) return 0;
01494 return ScanPath(this, extension, path, strlen(path), recursive);
01495 }