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
00032 #define FIO_BUFFER_SIZE 512
00033
00035 struct Fio {
00036 byte *buffer, *buffer_end;
00037 size_t pos;
00038 FILE *cur_fh;
00039 const char *filename;
00040 FILE *handles[MAX_FILE_SLOTS];
00041 byte buffer_start[FIO_BUFFER_SIZE];
00042 const char *filenames[MAX_FILE_SLOTS];
00043 char *shortnames[MAX_FILE_SLOTS];
00044 #if defined(LIMITED_FDS)
00045 uint open_handles;
00046 uint usage_count[MAX_FILE_SLOTS];
00047 #endif
00048 };
00049
00050 static Fio _fio;
00051
00053 static bool _do_scan_working_directory = true;
00054
00055 extern char *_config_file;
00056 extern char *_highscore_file;
00057
00062 size_t FioGetPos()
00063 {
00064 return _fio.pos + (_fio.buffer - _fio.buffer_end);
00065 }
00066
00072 const char *FioGetFilename(uint8 slot)
00073 {
00074 return _fio.shortnames[slot];
00075 }
00076
00082 void FioSeekTo(size_t pos, int mode)
00083 {
00084 if (mode == SEEK_CUR) pos += FioGetPos();
00085 _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
00086 _fio.pos = pos;
00087 fseek(_fio.cur_fh, _fio.pos, SEEK_SET);
00088 }
00089
00090 #if defined(LIMITED_FDS)
00091 static void FioRestoreFile(int slot)
00092 {
00093
00094 if (_fio.handles[slot] == NULL) {
00095 DEBUG(misc, 6, "Restoring file '%s' in slot '%d' from disk", _fio.filenames[slot], slot);
00096 FioOpenFile(slot, _fio.filenames[slot]);
00097 }
00098 _fio.usage_count[slot]++;
00099 }
00100 #endif
00101
00107 void FioSeekToFile(uint8 slot, size_t pos)
00108 {
00109 FILE *f;
00110 #if defined(LIMITED_FDS)
00111
00112 FioRestoreFile(slot);
00113 #endif
00114 f = _fio.handles[slot];
00115 assert(f != NULL);
00116 _fio.cur_fh = f;
00117 _fio.filename = _fio.filenames[slot];
00118 FioSeekTo(pos, SEEK_SET);
00119 }
00120
00125 byte FioReadByte()
00126 {
00127 if (_fio.buffer == _fio.buffer_end) {
00128 _fio.buffer = _fio.buffer_start;
00129 size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
00130 _fio.pos += size;
00131 _fio.buffer_end = _fio.buffer_start + size;
00132
00133 if (size == 0) return 0;
00134 }
00135 return *_fio.buffer++;
00136 }
00137
00142 void FioSkipBytes(int n)
00143 {
00144 for (;;) {
00145 int m = min(_fio.buffer_end - _fio.buffer, n);
00146 _fio.buffer += m;
00147 n -= m;
00148 if (n == 0) break;
00149 FioReadByte();
00150 n--;
00151 }
00152 }
00153
00158 uint16 FioReadWord()
00159 {
00160 byte b = FioReadByte();
00161 return (FioReadByte() << 8) | b;
00162 }
00163
00168 uint32 FioReadDword()
00169 {
00170 uint b = FioReadWord();
00171 return (FioReadWord() << 16) | b;
00172 }
00173
00179 void FioReadBlock(void *ptr, size_t size)
00180 {
00181 FioSeekTo(FioGetPos(), SEEK_SET);
00182 _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
00183 }
00184
00189 static inline void FioCloseFile(int slot)
00190 {
00191 if (_fio.handles[slot] != NULL) {
00192 fclose(_fio.handles[slot]);
00193
00194 free(_fio.shortnames[slot]);
00195 _fio.shortnames[slot] = NULL;
00196
00197 _fio.handles[slot] = NULL;
00198 #if defined(LIMITED_FDS)
00199 _fio.open_handles--;
00200 #endif
00201 }
00202 }
00203
00205 void FioCloseAll()
00206 {
00207 for (int i = 0; i != lengthof(_fio.handles); i++) {
00208 FioCloseFile(i);
00209 }
00210 }
00211
00212 #if defined(LIMITED_FDS)
00213 static void FioFreeHandle()
00214 {
00215
00216 if (_fio.open_handles + 1 == LIMITED_FDS) {
00217 uint i, count;
00218 int slot;
00219
00220 count = UINT_MAX;
00221 slot = -1;
00222
00223 for (i = 0; i < lengthof(_fio.handles); i++) {
00224 if (_fio.handles[i] != NULL && _fio.usage_count[i] < count) {
00225 count = _fio.usage_count[i];
00226 slot = i;
00227 }
00228 }
00229 assert(slot != -1);
00230 DEBUG(misc, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.filenames[slot], slot);
00231 FioCloseFile(slot);
00232 }
00233 }
00234 #endif
00235
00242 void FioOpenFile(int slot, const char *filename, Subdirectory subdir)
00243 {
00244 FILE *f;
00245
00246 #if defined(LIMITED_FDS)
00247 FioFreeHandle();
00248 #endif
00249 f = FioFOpenFile(filename, "rb", subdir);
00250 if (f == NULL) usererror("Cannot open file '%s'", filename);
00251 uint32 pos = ftell(f);
00252
00253 FioCloseFile(slot);
00254 _fio.handles[slot] = f;
00255 _fio.filenames[slot] = filename;
00256
00257
00258 const char *t = strrchr(filename, PATHSEPCHAR);
00259 _fio.shortnames[slot] = strdup(t == NULL ? filename : t);
00260 char *t2 = strrchr(_fio.shortnames[slot], '.');
00261 if (t2 != NULL) *t2 = '\0';
00262 strtolower(_fio.shortnames[slot]);
00263
00264 #if defined(LIMITED_FDS)
00265 _fio.usage_count[slot] = 0;
00266 _fio.open_handles++;
00267 #endif
00268 FioSeekToFile(slot, pos);
00269 }
00270
00271 static const char * const _subdirs[] = {
00272 "",
00273 "save" PATHSEP,
00274 "save" PATHSEP "autosave" PATHSEP,
00275 "scenario" PATHSEP,
00276 "scenario" PATHSEP "heightmap" PATHSEP,
00277 "gm" PATHSEP,
00278 "data" PATHSEP,
00279 "baseset" PATHSEP,
00280 "newgrf" PATHSEP,
00281 "lang" PATHSEP,
00282 "ai" PATHSEP,
00283 "ai" PATHSEP "library" PATHSEP,
00284 };
00285 assert_compile(lengthof(_subdirs) == NUM_SUBDIRS);
00286
00287 const char *_searchpaths[NUM_SEARCHPATHS];
00288 TarList _tar_list[NUM_SUBDIRS];
00289 TarFileList _tar_filelist[NUM_SUBDIRS];
00290
00291 typedef std::map<std::string, std::string> TarLinkList;
00292 static TarLinkList _tar_linklist[NUM_SUBDIRS];
00293
00300 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
00301 {
00302 FILE *f = FioFOpenFile(filename, "rb", subdir);
00303 if (f == NULL) return false;
00304
00305 FioFCloseFile(f);
00306 return true;
00307 }
00308
00314 bool FileExists(const char *filename)
00315 {
00316 #if defined(WINCE)
00317
00318 HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
00319 if (hand == INVALID_HANDLE_VALUE) return 1;
00320 CloseHandle(hand);
00321 return 0;
00322 #else
00323 return access(OTTD2FS(filename), 0) == 0;
00324 #endif
00325 }
00326
00330 void FioFCloseFile(FILE *f)
00331 {
00332 fclose(f);
00333 }
00334
00335 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
00336 {
00337 assert(subdir < NUM_SUBDIRS);
00338 assert(sp < NUM_SEARCHPATHS);
00339
00340 snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00341 return buf;
00342 }
00343
00352 char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
00353 {
00354 Searchpath sp;
00355 assert(subdir < NUM_SUBDIRS);
00356
00357 FOR_ALL_SEARCHPATHS(sp) {
00358 FioGetFullPath(buf, buflen, sp, subdir, filename);
00359 if (FileExists(buf)) return buf;
00360 #if !defined(WIN32)
00361
00362
00363
00364 if (strtolower(buf + strlen(_searchpaths[sp]) - 1) && FileExists(buf)) return buf;
00365 #endif
00366 }
00367
00368 return NULL;
00369 }
00370
00371 char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
00372 {
00373 assert(subdir < NUM_SUBDIRS);
00374 assert(sp < NUM_SEARCHPATHS);
00375
00376 snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
00377 return buf;
00378 }
00379
00380 char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
00381 {
00382 Searchpath sp;
00383
00384
00385 FOR_ALL_SEARCHPATHS(sp) {
00386 char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
00387 if (FileExists(buf)) return ret;
00388 }
00389
00390
00391 ttd_strlcpy(buf, _personal_dir, buflen);
00392
00393 return buf;
00394 }
00395
00396 static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
00397 {
00398 #if defined(WIN32) && defined(UNICODE)
00399
00400
00401
00402
00403 wchar_t Lmode[5];
00404 MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
00405 #endif
00406 FILE *f = NULL;
00407 char buf[MAX_PATH];
00408
00409 if (subdir == NO_DIRECTORY) {
00410 strecpy(buf, filename, lastof(buf));
00411 } else {
00412 snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00413 }
00414
00415 #if defined(WIN32)
00416 if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
00417 #endif
00418
00419 f = fopen(buf, mode);
00420 #if !defined(WIN32)
00421 if (f == NULL && strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1))) {
00422 f = fopen(buf, mode);
00423 }
00424 #endif
00425 if (f != NULL && filesize != NULL) {
00426
00427 fseek(f, 0, SEEK_END);
00428 *filesize = ftell(f);
00429 fseek(f, 0, SEEK_SET);
00430 }
00431 return f;
00432 }
00433
00441 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
00442 {
00443 FILE *f = fopen(entry->tar_filename, "rb");
00444 if (f == NULL) return f;
00445
00446 fseek(f, entry->position, SEEK_SET);
00447 if (filesize != NULL) *filesize = entry->size;
00448 return f;
00449 }
00450
00458 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
00459 {
00460 FILE *f = NULL;
00461 Searchpath sp;
00462
00463 assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
00464
00465 FOR_ALL_SEARCHPATHS(sp) {
00466 f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
00467 if (f != NULL || subdir == NO_DIRECTORY) break;
00468 }
00469
00470
00471 if (f == NULL && mode[0] == 'r' && subdir != NO_DIRECTORY) {
00472 static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1;
00473 char resolved_name[MAX_RESOLVED_LENGTH];
00474
00475
00476 strecpy(resolved_name, filename, lastof(resolved_name));
00477 strtolower(resolved_name);
00478
00479 size_t resolved_len = strlen(resolved_name);
00480
00481
00482 for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
00483 const std::string &src = link->first;
00484 size_t len = src.length();
00485 if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
00486
00487 char resolved_name2[MAX_RESOLVED_LENGTH];
00488 const std::string &dest = link->second;
00489 strecpy(resolved_name2, &(resolved_name[len]), lastof(resolved_name2));
00490 strecpy(resolved_name, dest.c_str(), lastof(resolved_name));
00491 strecpy(&(resolved_name[dest.length()]), resolved_name2, lastof(resolved_name));
00492 break;
00493 }
00494 }
00495
00496 TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
00497 if (it != _tar_filelist[subdir].end()) {
00498 f = FioFOpenFileTar(&((*it).second), filesize);
00499 }
00500 }
00501
00502
00503
00504 if (f == NULL && subdir != NO_DIRECTORY) {
00505 switch (subdir) {
00506 case BASESET_DIR:
00507 f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
00508 if (f != NULL) break;
00509
00510 case NEWGRF_DIR:
00511 f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
00512 break;
00513
00514 default:
00515 f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
00516 break;
00517 }
00518 }
00519
00520 return f;
00521 }
00522
00527 static void FioCreateDirectory(const char *name)
00528 {
00529 #if defined(WIN32) || defined(WINCE)
00530 CreateDirectory(OTTD2FS(name), NULL);
00531 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
00532 mkdir(OTTD2FS(name));
00533 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
00534 char buf[MAX_PATH];
00535 ttd_strlcpy(buf, name, MAX_PATH);
00536
00537 size_t len = strlen(name) - 1;
00538 if (buf[len] == '/') {
00539 buf[len] = '\0';
00540 }
00541
00542 mkdir(OTTD2FS(buf), 0755);
00543 #else
00544 mkdir(OTTD2FS(name), 0755);
00545 #endif
00546 }
00547
00555 bool AppendPathSeparator(char *buf, size_t buflen)
00556 {
00557 size_t s = strlen(buf);
00558
00559
00560 if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
00561 if (s + 2 >= buflen) return false;
00562
00563 buf[s] = PATHSEPCHAR;
00564 buf[s + 1] = '\0';
00565 }
00566
00567 return true;
00568 }
00569
00576 char *BuildWithFullPath(const char *dir)
00577 {
00578 char *dest = MallocT<char>(MAX_PATH);
00579 ttd_strlcpy(dest, dir, MAX_PATH);
00580
00581
00582 const char *s = strchr(dest, PATHSEPCHAR);
00583
00584
00585 if (s == NULL || dest != s) {
00586 if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0';
00587 AppendPathSeparator(dest, MAX_PATH);
00588 ttd_strlcat(dest, dir, MAX_PATH);
00589 }
00590 AppendPathSeparator(dest, MAX_PATH);
00591
00592 return dest;
00593 }
00594
00600 const char *FioTarFirstDir(const char *tarname, Subdirectory subdir)
00601 {
00602 TarList::iterator it = _tar_list[subdir].find(tarname);
00603 if (it == _tar_list[subdir].end()) return NULL;
00604 return (*it).second.dirname;
00605 }
00606
00607 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
00608 {
00609 std::string src = srcParam;
00610 std::string dest = destParam;
00611
00612 std::transform(src.begin(), src.end(), src.begin(), tolower);
00613 std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
00614
00615 TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
00616 if (dest_file != _tar_filelist[subdir].end()) {
00617
00618 _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
00619 } else {
00620
00621
00622 const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
00623 const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
00624 _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
00625 }
00626 }
00627
00628 void FioTarAddLink(const char *src, const char *dest, Subdirectory subdir)
00629 {
00630 TarAddLink(src, dest, subdir);
00631 }
00632
00638 static void SimplifyFileName(char *name)
00639 {
00640
00641 strtolower(name);
00642
00643
00644 #if (PATHSEPCHAR != '/')
00645 for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
00646 #endif
00647 }
00648
00654 uint TarScanner::DoScan(Subdirectory sd)
00655 {
00656 _tar_filelist[sd].clear();
00657 _tar_list[sd].clear();
00658 uint num = this->Scan(".tar", sd, false);
00659 if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
00660 return num;
00661 }
00662
00663 uint TarScanner::DoScan(TarScanner::Mode mode)
00664 {
00665 DEBUG(misc, 1, "Scanning for tars");
00666 TarScanner fs;
00667 uint num = 0;
00668 if (mode & TarScanner::BASESET) {
00669 num += fs.DoScan(BASESET_DIR);
00670 }
00671 if (mode & TarScanner::NEWGRF) {
00672 num += fs.DoScan(NEWGRF_DIR);
00673 }
00674 if (mode & TarScanner::AI) {
00675 num += fs.DoScan(AI_DIR);
00676 num += fs.DoScan(AI_LIBRARY_DIR);
00677 }
00678 if (mode & TarScanner::SCENARIO) {
00679 num += fs.DoScan(SCENARIO_DIR);
00680 }
00681 DEBUG(misc, 1, "Scan complete, found %d files", num);
00682 return num;
00683 }
00684
00691 bool TarScanner::AddFile(Subdirectory sd, const char *filename)
00692 {
00693 this->subdir = sd;
00694 return this->AddFile(filename, 0);
00695 }
00696
00697 bool TarScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00698 {
00699
00700 assert(tar_filename == NULL);
00701
00702
00703 typedef struct TarHeader {
00704 char name[100];
00705 char mode[8];
00706 char uid[8];
00707 char gid[8];
00708 char size[12];
00709 char mtime[12];
00710 char chksum[8];
00711 char typeflag;
00712 char linkname[100];
00713 char magic[6];
00714 char version[2];
00715 char uname[32];
00716 char gname[32];
00717 char devmajor[8];
00718 char devminor[8];
00719 char prefix[155];
00720
00721 char unused[12];
00722 } TarHeader;
00723
00724
00725 TarList::iterator it = _tar_list[this->subdir].find(filename);
00726 if (it != _tar_list[this->subdir].end()) return false;
00727
00728 FILE *f = fopen(filename, "rb");
00729
00730
00731
00732
00733 if (f == NULL) return false;
00734
00735 const char *dupped_filename = strdup(filename);
00736 _tar_list[this->subdir][filename].filename = dupped_filename;
00737 _tar_list[this->subdir][filename].dirname = NULL;
00738
00739 TarLinkList links;
00740
00741 TarHeader th;
00742 char buf[sizeof(th.name) + 1], *end;
00743 char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
00744 char link[sizeof(th.linkname) + 1];
00745 char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
00746 size_t num = 0, pos = 0;
00747
00748
00749 char empty[512];
00750 memset(&empty[0], 0, sizeof(empty));
00751
00752 for (;;) {
00753 size_t num_bytes_read = fread(&th, 1, 512, f);
00754 if (num_bytes_read != 512) break;
00755 pos += num_bytes_read;
00756
00757
00758 if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
00759
00760 if (memcmp(&th, &empty[0], 512) == 0) continue;
00761
00762 DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
00763 return false;
00764 }
00765
00766 name[0] = '\0';
00767 size_t len = 0;
00768
00769
00770 if (th.prefix[0] != '\0') {
00771 memcpy(name, th.prefix, sizeof(th.prefix));
00772 name[sizeof(th.prefix)] = '\0';
00773 len = strlen(name);
00774 name[len] = PATHSEPCHAR;
00775 len++;
00776 }
00777
00778
00779 memcpy(&name[len], th.name, sizeof(th.name));
00780 name[len + sizeof(th.name)] = '\0';
00781
00782
00783 memcpy(buf, th.size, sizeof(th.size));
00784 buf[sizeof(th.size)] = '\0';
00785 size_t skip = strtoul(buf, &end, 8);
00786
00787 switch (th.typeflag) {
00788 case '\0':
00789 case '0': {
00790
00791 if (skip == 0) break;
00792
00793 if (strlen(name) == 0) break;
00794
00795
00796 TarFileListEntry entry;
00797 entry.tar_filename = dupped_filename;
00798 entry.size = skip;
00799 entry.position = pos;
00800
00801
00802 SimplifyFileName(name);
00803
00804 DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
00805 if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
00806
00807 break;
00808 }
00809
00810 case '1':
00811 case '2': {
00812
00813 memcpy(link, th.linkname, sizeof(th.linkname));
00814 link[sizeof(th.linkname)] = '\0';
00815
00816 if (strlen(name) == 0 || strlen(link) == 0) break;
00817
00818
00819 SimplifyFileName(name);
00820 SimplifyFileName(link);
00821
00822
00823 if (link[0] == PATHSEPCHAR) {
00824 DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
00825 break;
00826 }
00827
00828
00829
00830 strecpy(dest, name, lastof(dest));
00831 char *destpos = strrchr(dest, PATHSEPCHAR);
00832 if (destpos == NULL) destpos = dest;
00833 *destpos = '\0';
00834
00835 char *pos = link;
00836 while (*pos != '\0') {
00837 char *next = strchr(link, PATHSEPCHAR);
00838 if (next == NULL) next = pos + strlen(pos);
00839
00840
00841 if (next != pos + 1 || pos[0] != '.') {
00842 if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') {
00843
00844 if (dest[0] == '\0') {
00845 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
00846 break;
00847 }
00848
00849
00850
00851 destpos = strrchr(dest, PATHSEPCHAR);
00852 if (destpos == NULL) destpos = dest;
00853 } else {
00854
00855 if (destpos != dest) *(destpos++) = PATHSEPCHAR;
00856 strncpy(destpos, pos, next - pos);
00857 destpos += next - pos;
00858 }
00859 *destpos = '\0';
00860 }
00861
00862 pos = next;
00863 }
00864
00865
00866 DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
00867 links.insert(TarLinkList::value_type(name, dest));
00868
00869 break;
00870 }
00871
00872 case '5':
00873
00874 SimplifyFileName(name);
00875
00876
00877 DEBUG(misc, 6, "Found dir in tar: %s", name);
00878 if (_tar_list[this->subdir][filename].dirname == NULL) _tar_list[this->subdir][filename].dirname = strdup(name);
00879 break;
00880
00881 default:
00882
00883 break;
00884 }
00885
00886
00887 skip = Align(skip, 512);
00888 fseek(f, skip, SEEK_CUR);
00889 pos += skip;
00890 }
00891
00892 DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
00893 fclose(f);
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
00905 const std::string &src = link->first;
00906 const std::string &dest = link->second;
00907 TarAddLink(src, dest, this->subdir);
00908 }
00909
00910 return true;
00911 }
00912
00920 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
00921 {
00922 TarList::iterator it = _tar_list[subdir].find(tar_filename);
00923
00924 if (it == _tar_list[subdir].end()) return false;
00925
00926 const char *dirname = (*it).second.dirname;
00927
00928
00929 if (dirname == NULL) return false;
00930
00931 char filename[MAX_PATH];
00932 strecpy(filename, tar_filename, lastof(filename));
00933 char *p = strrchr(filename, PATHSEPCHAR);
00934
00935 if (p == NULL) return false;
00936
00937 p++;
00938 strecpy(p, dirname, lastof(filename));
00939 DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
00940 FioCreateDirectory(filename);
00941
00942 for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
00943 if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
00944
00945 strecpy(p, (*it2).first.c_str(), lastof(filename));
00946
00947 DEBUG(misc, 9, " extracting %s", filename);
00948
00949
00950 size_t to_copy = 0;
00951 FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
00952 if (in == NULL) {
00953 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
00954 return false;
00955 }
00956
00957
00958 FILE *out = fopen(filename, "wb");
00959 if (out == NULL) {
00960 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
00961 fclose(in);
00962 return false;
00963 }
00964
00965
00966 char buffer[4096];
00967 size_t read;
00968 for (; to_copy != 0; to_copy -= read) {
00969 read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
00970 if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
00971 }
00972
00973
00974 fclose(in);
00975 fclose(out);
00976
00977 if (to_copy != 0) {
00978 DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
00979 return false;
00980 }
00981 }
00982
00983 DEBUG(misc, 9, " extraction successful");
00984 return true;
00985 }
00986
00987 #if defined(WIN32) || defined(WINCE)
00988
00993 extern void DetermineBasePaths(const char *exe);
00994 #else
00995
01003 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
01004 {
01005 bool success = false;
01006 #ifdef WITH_COCOA
01007 char *app_bundle = strchr(exe, '.');
01008 while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
01009
01010 if (app_bundle != NULL) app_bundle[0] = '\0';
01011 #endif
01012 char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
01013 if (s != NULL) {
01014 *s = '\0';
01015 #if defined(__DJGPP__)
01016
01017 if (s[-1] == ':') chdir("/");
01018 #endif
01019 if (chdir(exe) != 0) {
01020 DEBUG(misc, 0, "Directory with the binary does not exist?");
01021 } else {
01022 success = true;
01023 }
01024 *s = PATHSEPCHAR;
01025 }
01026 #ifdef WITH_COCOA
01027 if (app_bundle != NULL) app_bundle[0] = '.';
01028 #endif
01029 return success;
01030 }
01031
01042 bool DoScanWorkingDirectory()
01043 {
01044
01045 if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
01046
01047
01048 if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
01049
01050
01051 if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
01052
01053 char tmp[MAX_PATH];
01054 snprintf(tmp, lengthof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
01055 AppendPathSeparator(tmp, MAX_PATH);
01056 return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
01057 }
01058
01063 void DetermineBasePaths(const char *exe)
01064 {
01065 char tmp[MAX_PATH];
01066 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
01067 _searchpaths[SP_PERSONAL_DIR] = NULL;
01068 #else
01069 #ifdef __HAIKU__
01070 BPath path;
01071 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
01072 const char *homedir = path.Path();
01073 #else
01074 const char *homedir = getenv("HOME");
01075
01076 if (homedir == NULL) {
01077 const struct passwd *pw = getpwuid(getuid());
01078 homedir = (pw == NULL) ? "" : pw->pw_dir;
01079 }
01080 #endif
01081
01082 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
01083 AppendPathSeparator(tmp, MAX_PATH);
01084
01085 _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
01086 #endif
01087
01088 #if defined(WITH_SHARED_DIR)
01089 snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
01090 AppendPathSeparator(tmp, MAX_PATH);
01091 _searchpaths[SP_SHARED_DIR] = strdup(tmp);
01092 #else
01093 _searchpaths[SP_SHARED_DIR] = NULL;
01094 #endif
01095
01096 #if defined(__MORPHOS__) || defined(__AMIGA__)
01097 _searchpaths[SP_WORKING_DIR] = NULL;
01098 #else
01099 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01100 AppendPathSeparator(tmp, MAX_PATH);
01101 _searchpaths[SP_WORKING_DIR] = strdup(tmp);
01102 #endif
01103
01104 _do_scan_working_directory = DoScanWorkingDirectory();
01105
01106
01107 if (ChangeWorkingDirectoryToExecutable(exe)) {
01108 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01109 AppendPathSeparator(tmp, MAX_PATH);
01110 _searchpaths[SP_BINARY_DIR] = strdup(tmp);
01111 } else {
01112 _searchpaths[SP_BINARY_DIR] = NULL;
01113 }
01114
01115 if (_searchpaths[SP_WORKING_DIR] != NULL) {
01116
01117 if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
01118 DEBUG(misc, 0, "Failed to return to working directory!");
01119 }
01120 }
01121
01122 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
01123 _searchpaths[SP_INSTALLATION_DIR] = NULL;
01124 #else
01125 snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
01126 AppendPathSeparator(tmp, MAX_PATH);
01127 _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
01128 #endif
01129 #ifdef WITH_COCOA
01130 extern void cocoaSetApplicationBundleDir();
01131 cocoaSetApplicationBundleDir();
01132 #else
01133 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
01134 #endif
01135 }
01136 #endif
01137
01138 char *_personal_dir;
01139
01146 void DeterminePaths(const char *exe)
01147 {
01148 DetermineBasePaths(exe);
01149
01150 Searchpath sp;
01151 FOR_ALL_SEARCHPATHS(sp) {
01152 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01153 DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
01154 }
01155
01156 if (_config_file != NULL) {
01157 _personal_dir = strdup(_config_file);
01158 char *end = strrchr(_personal_dir, PATHSEPCHAR);
01159 if (end == NULL) {
01160 _personal_dir[0] = '\0';
01161 } else {
01162 end[1] = '\0';
01163 }
01164 } else {
01165 char personal_dir[MAX_PATH];
01166 if (FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg") != NULL) {
01167 char *end = strrchr(personal_dir, PATHSEPCHAR);
01168 if (end != NULL) end[1] = '\0';
01169 _personal_dir = strdup(personal_dir);
01170 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01171 } else {
01172 static const Searchpath new_openttd_cfg_order[] = {
01173 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
01174 };
01175
01176 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
01177 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
01178 _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
01179 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01180 break;
01181 }
01182 }
01183 }
01184 }
01185
01186 DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
01187
01188 _highscore_file = str_fmt("%shs.dat", _personal_dir);
01189 extern char *_hotkeys_file;
01190 _hotkeys_file = str_fmt("%shotkeys.cfg", _personal_dir);
01191
01192
01193 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
01194 FioCreateDirectory(_personal_dir);
01195 #endif
01196
01197 static const Subdirectory default_subdirs[] = {
01198 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR
01199 };
01200
01201 for (uint i = 0; i < lengthof(default_subdirs); i++) {
01202 char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
01203 FioCreateDirectory(dir);
01204 free(dir);
01205 }
01206
01207
01208 _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
01209 #ifdef ENABLE_NETWORK
01210 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01211
01212
01213 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR };
01214 for (uint i = 0; i < lengthof(dirs); i++) {
01215 char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
01216 FioCreateDirectory(tmp);
01217 free(tmp);
01218 }
01219
01220 extern char *_log_file;
01221 _log_file = str_fmt("%sopenttd.log", _personal_dir);
01222 #else
01223
01224
01225 if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
01226 free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01227 _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
01228 }
01229 #endif
01230 }
01231
01236 void SanitizeFilename(char *filename)
01237 {
01238 for (; *filename != '\0'; filename++) {
01239 switch (*filename) {
01240
01241
01242 case ':': case '\\': case '*': case '?': case '/':
01243 case '<': case '>': case '|': case '"':
01244 *filename = '_';
01245 break;
01246 }
01247 }
01248 }
01249
01258 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
01259 {
01260 FILE *in = fopen(filename, "rb");
01261 if (in == NULL) return NULL;
01262
01263 fseek(in, 0, SEEK_END);
01264 size_t len = ftell(in);
01265 fseek(in, 0, SEEK_SET);
01266 if (len > maxsize) {
01267 fclose(in);
01268 return NULL;
01269 }
01270 byte *mem = MallocT<byte>(len + 1);
01271 mem[len] = 0;
01272 if (fread(mem, len, 1, in) != 1) {
01273 fclose(in);
01274 free(mem);
01275 return NULL;
01276 }
01277 fclose(in);
01278
01279 *lenp = len;
01280 return mem;
01281 }
01282
01289 static bool MatchesExtension(const char *extension, const char *filename)
01290 {
01291 if (extension == NULL) return true;
01292
01293 const char *ext = strrchr(filename, extension[0]);
01294 return ext != NULL && strcasecmp(ext, extension) == 0;
01295 }
01296
01306 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
01307 {
01308 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
01309
01310 uint num = 0;
01311 struct stat sb;
01312 struct dirent *dirent;
01313 DIR *dir;
01314
01315 if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
01316
01317 while ((dirent = readdir(dir)) != NULL) {
01318 const char *d_name = FS2OTTD(dirent->d_name);
01319 char filename[MAX_PATH];
01320
01321 if (!FiosIsValidFile(path, dirent, &sb)) continue;
01322
01323 snprintf(filename, lengthof(filename), "%s%s", path, d_name);
01324
01325 if (S_ISDIR(sb.st_mode)) {
01326
01327 if (!recursive) continue;
01328 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01329 if (!AppendPathSeparator(filename, lengthof(filename))) continue;
01330 num += ScanPath(fs, extension, filename, basepath_length, recursive);
01331 } else if (S_ISREG(sb.st_mode)) {
01332
01333 if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, NULL)) num++;
01334 }
01335 }
01336
01337 closedir(dir);
01338
01339 return num;
01340 }
01341
01348 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01349 {
01350 uint num = 0;
01351 const char *filename = (*tar).first.c_str();
01352
01353 if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
01354
01355 return num;
01356 }
01357
01367 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
01368 {
01369 this->subdir = sd;
01370
01371 Searchpath sp;
01372 char path[MAX_PATH];
01373 TarFileList::iterator tar;
01374 uint num = 0;
01375
01376 FOR_ALL_SEARCHPATHS(sp) {
01377
01378 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01379
01380 FioAppendDirectory(path, MAX_PATH, sp, sd);
01381 num += ScanPath(this, extension, path, strlen(path), recursive);
01382 }
01383
01384 if (tars && sd != NO_DIRECTORY) {
01385 FOR_ALL_TARS(tar, sd) {
01386 num += ScanTar(this, extension, tar);
01387 }
01388 }
01389
01390 switch (sd) {
01391 case BASESET_DIR:
01392 num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
01393
01394 case NEWGRF_DIR:
01395 num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
01396 break;
01397
01398 default: break;
01399 }
01400
01401 return num;
01402 }
01403
01412 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
01413 {
01414 char path[MAX_PATH];
01415 strecpy(path, directory, lastof(path));
01416 if (!AppendPathSeparator(path, lengthof(path))) return 0;
01417 return ScanPath(this, extension, path, strlen(path), recursive);
01418 }