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(pos, PATHSEPCHAR);
00853 if (next == NULL) {
00854 next = pos + strlen(pos);
00855 } else {
00856
00857 *next++= '\0';
00858 }
00859
00860 if (strcmp(pos, ".") == 0) {
00861
00862 } else if (strcmp(pos, "..") == 0) {
00863
00864 if (dest[0] == '\0') {
00865 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
00866 break;
00867 }
00868
00869
00870
00871 destpos = strrchr(dest, PATHSEPCHAR);
00872 if (destpos == NULL) destpos = dest;
00873 *destpos = '\0';
00874 } else {
00875
00876 if (destpos != dest) destpos = strecpy(destpos, PATHSEP, lastof(dest));
00877 destpos = strecpy(destpos, pos, lastof(dest));
00878 }
00879
00880 if (destpos >= lastof(dest)) {
00881 DEBUG(misc, 0, "The length of a link in tar-file '%s' is too large (malformed?)", filename);
00882 fclose(f);
00883 return false;
00884 }
00885
00886 pos = next;
00887 }
00888
00889
00890 DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
00891 links.insert(TarLinkList::value_type(name, dest));
00892
00893 break;
00894 }
00895
00896 case '5':
00897
00898 SimplifyFileName(name);
00899
00900
00901 DEBUG(misc, 6, "Found dir in tar: %s", name);
00902 if (_tar_list[this->subdir][filename].dirname == NULL) _tar_list[this->subdir][filename].dirname = strdup(name);
00903 break;
00904
00905 default:
00906
00907 break;
00908 }
00909
00910
00911 skip = Align(skip, 512);
00912 if (fseek(f, skip, SEEK_CUR) < 0) {
00913 DEBUG(misc, 0, "The file '%s' can't be read as a valid tar-file", filename);
00914 fclose(f);
00915 return false;
00916 }
00917 pos += skip;
00918 }
00919
00920 DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
00921 fclose(f);
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
00933 const std::string &src = link->first;
00934 const std::string &dest = link->second;
00935 TarAddLink(src, dest, this->subdir);
00936 }
00937
00938 return true;
00939 }
00940
00948 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
00949 {
00950 TarList::iterator it = _tar_list[subdir].find(tar_filename);
00951
00952 if (it == _tar_list[subdir].end()) return false;
00953
00954 const char *dirname = (*it).second.dirname;
00955
00956
00957 if (dirname == NULL) return false;
00958
00959 char filename[MAX_PATH];
00960 strecpy(filename, tar_filename, lastof(filename));
00961 char *p = strrchr(filename, PATHSEPCHAR);
00962
00963 if (p == NULL) return false;
00964
00965 p++;
00966 strecpy(p, dirname, lastof(filename));
00967 DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
00968 FioCreateDirectory(filename);
00969
00970 for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
00971 if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
00972
00973 strecpy(p, (*it2).first.c_str(), lastof(filename));
00974
00975 DEBUG(misc, 9, " extracting %s", filename);
00976
00977
00978 size_t to_copy = 0;
00979 FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
00980 if (in == NULL) {
00981 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
00982 return false;
00983 }
00984
00985
00986 FILE *out = fopen(filename, "wb");
00987 if (out == NULL) {
00988 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
00989 fclose(in);
00990 return false;
00991 }
00992
00993
00994 char buffer[4096];
00995 size_t read;
00996 for (; to_copy != 0; to_copy -= read) {
00997 read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
00998 if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
00999 }
01000
01001
01002 fclose(in);
01003 fclose(out);
01004
01005 if (to_copy != 0) {
01006 DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
01007 return false;
01008 }
01009 }
01010
01011 DEBUG(misc, 9, " extraction successful");
01012 return true;
01013 }
01014
01015 #if defined(WIN32) || defined(WINCE)
01016
01021 extern void DetermineBasePaths(const char *exe);
01022 #else
01023
01031 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
01032 {
01033 bool success = false;
01034 #ifdef WITH_COCOA
01035 char *app_bundle = strchr(exe, '.');
01036 while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
01037
01038 if (app_bundle != NULL) app_bundle[0] = '\0';
01039 #endif
01040 char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
01041 if (s != NULL) {
01042 *s = '\0';
01043 #if defined(__DJGPP__)
01044
01045 if (s[-1] == ':') chdir("/");
01046 #endif
01047 if (chdir(exe) != 0) {
01048 DEBUG(misc, 0, "Directory with the binary does not exist?");
01049 } else {
01050 success = true;
01051 }
01052 *s = PATHSEPCHAR;
01053 }
01054 #ifdef WITH_COCOA
01055 if (app_bundle != NULL) app_bundle[0] = '.';
01056 #endif
01057 return success;
01058 }
01059
01070 bool DoScanWorkingDirectory()
01071 {
01072
01073 if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
01074
01075
01076 if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
01077
01078
01079 if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
01080
01081 char tmp[MAX_PATH];
01082 snprintf(tmp, lengthof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
01083 AppendPathSeparator(tmp, MAX_PATH);
01084 return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
01085 }
01086
01091 void DetermineBasePaths(const char *exe)
01092 {
01093 char tmp[MAX_PATH];
01094 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
01095 const char *xdg_data_home = xdgDataHome(NULL);
01096 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", xdg_data_home,
01097 PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
01098 free(xdg_data_home);
01099
01100 AppendPathSeparator(tmp, MAX_PATH);
01101 _searchpaths[SP_PERSONAL_DIR_XDG] = strdup(tmp);
01102 #endif
01103 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
01104 _searchpaths[SP_PERSONAL_DIR] = NULL;
01105 #else
01106 #ifdef __HAIKU__
01107 BPath path;
01108 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
01109 const char *homedir = strdup(path.Path());
01110 #else
01111
01112
01113
01114
01115 const char *homedir = getenv("HOME");
01116 if (homedir != NULL) {
01117 homedir = strndup(homedir, MAX_PATH);
01118 }
01119
01120 if (homedir == NULL) {
01121 const struct passwd *pw = getpwuid(getuid());
01122 homedir = (pw == NULL) ? NULL : strdup(pw->pw_dir);
01123 }
01124 #endif
01125
01126 if (homedir != NULL) {
01127 ValidateString(homedir);
01128 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
01129 AppendPathSeparator(tmp, MAX_PATH);
01130
01131 _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
01132 free(homedir);
01133 } else {
01134 _searchpaths[SP_PERSONAL_DIR] = NULL;
01135 }
01136 #endif
01137
01138 #if defined(WITH_SHARED_DIR)
01139 snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
01140 AppendPathSeparator(tmp, MAX_PATH);
01141 _searchpaths[SP_SHARED_DIR] = strdup(tmp);
01142 #else
01143 _searchpaths[SP_SHARED_DIR] = NULL;
01144 #endif
01145
01146 #if defined(__MORPHOS__) || defined(__AMIGA__)
01147 _searchpaths[SP_WORKING_DIR] = NULL;
01148 #else
01149 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01150 AppendPathSeparator(tmp, MAX_PATH);
01151 _searchpaths[SP_WORKING_DIR] = strdup(tmp);
01152 #endif
01153
01154 _do_scan_working_directory = DoScanWorkingDirectory();
01155
01156
01157 if (ChangeWorkingDirectoryToExecutable(exe)) {
01158 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01159 AppendPathSeparator(tmp, MAX_PATH);
01160 _searchpaths[SP_BINARY_DIR] = strdup(tmp);
01161 } else {
01162 _searchpaths[SP_BINARY_DIR] = NULL;
01163 }
01164
01165 if (_searchpaths[SP_WORKING_DIR] != NULL) {
01166
01167 if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
01168 DEBUG(misc, 0, "Failed to return to working directory!");
01169 }
01170 }
01171
01172 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
01173 _searchpaths[SP_INSTALLATION_DIR] = NULL;
01174 #else
01175 snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
01176 AppendPathSeparator(tmp, MAX_PATH);
01177 _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
01178 #endif
01179 #ifdef WITH_COCOA
01180 extern void cocoaSetApplicationBundleDir();
01181 cocoaSetApplicationBundleDir();
01182 #else
01183 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
01184 #endif
01185 }
01186 #endif
01187
01188 const char *_personal_dir;
01189
01196 void DeterminePaths(const char *exe)
01197 {
01198 DetermineBasePaths(exe);
01199
01200 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
01201 char config_home[MAX_PATH];
01202
01203 const char *xdg_config_home = xdgConfigHome(NULL);
01204 snprintf(config_home, MAX_PATH, "%s" PATHSEP "%s", xdg_config_home,
01205 PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
01206 free(xdg_config_home);
01207
01208 AppendPathSeparator(config_home, MAX_PATH);
01209 #endif
01210
01211 Searchpath sp;
01212 FOR_ALL_SEARCHPATHS(sp) {
01213 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01214 DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
01215 }
01216
01217 char *config_dir;
01218 if (_config_file != NULL) {
01219 config_dir = strdup(_config_file);
01220 char *end = strrchr(config_dir, PATHSEPCHAR);
01221 if (end == NULL) {
01222 config_dir[0] = '\0';
01223 } else {
01224 end[1] = '\0';
01225 }
01226 } else {
01227 char personal_dir[MAX_PATH];
01228 if (FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg") != NULL) {
01229 char *end = strrchr(personal_dir, PATHSEPCHAR);
01230 if (end != NULL) end[1] = '\0';
01231 config_dir = strdup(personal_dir);
01232 _config_file = str_fmt("%sopenttd.cfg", config_dir);
01233 } else {
01234 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
01235
01236 config_dir = config_home;
01237 #else
01238 static const Searchpath new_openttd_cfg_order[] = {
01239 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
01240 };
01241
01242 config_dir = NULL;
01243 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
01244 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
01245 config_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
01246 break;
01247 }
01248 }
01249 assert(config_dir != NULL);
01250 #endif
01251 _config_file = str_fmt("%sopenttd.cfg", config_dir);
01252 }
01253 }
01254
01255 DEBUG(misc, 3, "%s found as config directory", config_dir);
01256
01257 _highscore_file = str_fmt("%shs.dat", config_dir);
01258 extern char *_hotkeys_file;
01259 _hotkeys_file = str_fmt("%shotkeys.cfg", config_dir);
01260 extern char *_windows_file;
01261 _windows_file = str_fmt("%swindows.cfg", config_dir);
01262
01263 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
01264 if (config_dir == config_home) {
01265
01266
01267 _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
01268 FioCreateDirectory(_personal_dir);
01269 } else
01270 #endif
01271 {
01272 _personal_dir = config_dir;
01273 }
01274
01275
01276 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
01277 FioCreateDirectory(config_dir);
01278 if (config_dir != _personal_dir) FioCreateDirectory(_personal_dir);
01279 #endif
01280
01281 DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
01282
01283 static const Subdirectory default_subdirs[] = {
01284 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR
01285 };
01286
01287 for (uint i = 0; i < lengthof(default_subdirs); i++) {
01288 char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
01289 FioCreateDirectory(dir);
01290 free(dir);
01291 }
01292
01293
01294 _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
01295 #ifdef ENABLE_NETWORK
01296 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01297
01298
01299 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };
01300 for (uint i = 0; i < lengthof(dirs); i++) {
01301 char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
01302 FioCreateDirectory(tmp);
01303 free(tmp);
01304 }
01305
01306 extern char *_log_file;
01307 _log_file = str_fmt("%sopenttd.log", _personal_dir);
01308 #else
01309
01310
01311 if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
01312 free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01313 _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
01314 }
01315 #endif
01316 }
01317
01322 void SanitizeFilename(char *filename)
01323 {
01324 for (; *filename != '\0'; filename++) {
01325 switch (*filename) {
01326
01327
01328 case ':': case '\\': case '*': case '?': case '/':
01329 case '<': case '>': case '|': case '"':
01330 *filename = '_';
01331 break;
01332 }
01333 }
01334 }
01335
01344 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
01345 {
01346 FILE *in = fopen(filename, "rb");
01347 if (in == NULL) return NULL;
01348
01349 fseek(in, 0, SEEK_END);
01350 size_t len = ftell(in);
01351 fseek(in, 0, SEEK_SET);
01352 if (len > maxsize) {
01353 fclose(in);
01354 return NULL;
01355 }
01356 byte *mem = MallocT<byte>(len + 1);
01357 mem[len] = 0;
01358 if (fread(mem, len, 1, in) != 1) {
01359 fclose(in);
01360 free(mem);
01361 return NULL;
01362 }
01363 fclose(in);
01364
01365 *lenp = len;
01366 return mem;
01367 }
01368
01375 static bool MatchesExtension(const char *extension, const char *filename)
01376 {
01377 if (extension == NULL) return true;
01378
01379 const char *ext = strrchr(filename, extension[0]);
01380 return ext != NULL && strcasecmp(ext, extension) == 0;
01381 }
01382
01392 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
01393 {
01394 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
01395
01396 uint num = 0;
01397 struct stat sb;
01398 struct dirent *dirent;
01399 DIR *dir;
01400
01401 if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
01402
01403 while ((dirent = readdir(dir)) != NULL) {
01404 const char *d_name = FS2OTTD(dirent->d_name);
01405 char filename[MAX_PATH];
01406
01407 if (!FiosIsValidFile(path, dirent, &sb)) continue;
01408
01409 snprintf(filename, lengthof(filename), "%s%s", path, d_name);
01410
01411 if (S_ISDIR(sb.st_mode)) {
01412
01413 if (!recursive) continue;
01414 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01415 if (!AppendPathSeparator(filename, lengthof(filename))) continue;
01416 num += ScanPath(fs, extension, filename, basepath_length, recursive);
01417 } else if (S_ISREG(sb.st_mode)) {
01418
01419 if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, NULL)) num++;
01420 }
01421 }
01422
01423 closedir(dir);
01424
01425 return num;
01426 }
01427
01434 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01435 {
01436 uint num = 0;
01437 const char *filename = (*tar).first.c_str();
01438
01439 if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
01440
01441 return num;
01442 }
01443
01453 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
01454 {
01455 this->subdir = sd;
01456
01457 Searchpath sp;
01458 char path[MAX_PATH];
01459 TarFileList::iterator tar;
01460 uint num = 0;
01461
01462 FOR_ALL_SEARCHPATHS(sp) {
01463
01464 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01465
01466 FioAppendDirectory(path, MAX_PATH, sp, sd);
01467 num += ScanPath(this, extension, path, strlen(path), recursive);
01468 }
01469
01470 if (tars && sd != NO_DIRECTORY) {
01471 FOR_ALL_TARS(tar, sd) {
01472 num += ScanTar(this, extension, tar);
01473 }
01474 }
01475
01476 switch (sd) {
01477 case BASESET_DIR:
01478 num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
01479
01480 case NEWGRF_DIR:
01481 num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
01482 break;
01483
01484 default: break;
01485 }
01486
01487 return num;
01488 }
01489
01498 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
01499 {
01500 char path[MAX_PATH];
01501 strecpy(path, directory, lastof(path));
01502 if (!AppendPathSeparator(path, lengthof(path))) return 0;
01503 return ScanPath(this, extension, path, strlen(path), recursive);
01504 }