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 "game" PATHSEP,
00285 "game" PATHSEP "library" PATHSEP,
00286 };
00287 assert_compile(lengthof(_subdirs) == NUM_SUBDIRS);
00288
00289 const char *_searchpaths[NUM_SEARCHPATHS];
00290 TarList _tar_list[NUM_SUBDIRS];
00291 TarFileList _tar_filelist[NUM_SUBDIRS];
00292
00293 typedef std::map<std::string, std::string> TarLinkList;
00294 static TarLinkList _tar_linklist[NUM_SUBDIRS];
00295
00302 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
00303 {
00304 FILE *f = FioFOpenFile(filename, "rb", subdir);
00305 if (f == NULL) return false;
00306
00307 FioFCloseFile(f);
00308 return true;
00309 }
00310
00316 bool FileExists(const char *filename)
00317 {
00318 #if defined(WINCE)
00319
00320 HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
00321 if (hand == INVALID_HANDLE_VALUE) return 1;
00322 CloseHandle(hand);
00323 return 0;
00324 #else
00325 return access(OTTD2FS(filename), 0) == 0;
00326 #endif
00327 }
00328
00332 void FioFCloseFile(FILE *f)
00333 {
00334 fclose(f);
00335 }
00336
00337 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
00338 {
00339 assert(subdir < NUM_SUBDIRS);
00340 assert(sp < NUM_SEARCHPATHS);
00341
00342 snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00343 return buf;
00344 }
00345
00354 char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
00355 {
00356 Searchpath sp;
00357 assert(subdir < NUM_SUBDIRS);
00358
00359 FOR_ALL_SEARCHPATHS(sp) {
00360 FioGetFullPath(buf, buflen, sp, subdir, filename);
00361 if (FileExists(buf)) return buf;
00362 #if !defined(WIN32)
00363
00364
00365
00366 if (strtolower(buf + strlen(_searchpaths[sp]) - 1) && FileExists(buf)) return buf;
00367 #endif
00368 }
00369
00370 return NULL;
00371 }
00372
00373 char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
00374 {
00375 assert(subdir < NUM_SUBDIRS);
00376 assert(sp < NUM_SEARCHPATHS);
00377
00378 snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
00379 return buf;
00380 }
00381
00382 char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
00383 {
00384 Searchpath sp;
00385
00386
00387 FOR_ALL_SEARCHPATHS(sp) {
00388 char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
00389 if (FileExists(buf)) return ret;
00390 }
00391
00392
00393 ttd_strlcpy(buf, _personal_dir, buflen);
00394
00395 return buf;
00396 }
00397
00398 static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
00399 {
00400 #if defined(WIN32) && defined(UNICODE)
00401
00402
00403
00404
00405 wchar_t Lmode[5];
00406 MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
00407 #endif
00408 FILE *f = NULL;
00409 char buf[MAX_PATH];
00410
00411 if (subdir == NO_DIRECTORY) {
00412 strecpy(buf, filename, lastof(buf));
00413 } else {
00414 snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00415 }
00416
00417 #if defined(WIN32)
00418 if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
00419 #endif
00420
00421 f = fopen(buf, mode);
00422 #if !defined(WIN32)
00423 if (f == NULL && strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1))) {
00424 f = fopen(buf, mode);
00425 }
00426 #endif
00427 if (f != NULL && filesize != NULL) {
00428
00429 fseek(f, 0, SEEK_END);
00430 *filesize = ftell(f);
00431 fseek(f, 0, SEEK_SET);
00432 }
00433 return f;
00434 }
00435
00443 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
00444 {
00445 FILE *f = fopen(entry->tar_filename, "rb");
00446 if (f == NULL) return f;
00447
00448 fseek(f, entry->position, SEEK_SET);
00449 if (filesize != NULL) *filesize = entry->size;
00450 return f;
00451 }
00452
00460 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
00461 {
00462 FILE *f = NULL;
00463 Searchpath sp;
00464
00465 assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
00466
00467 FOR_ALL_SEARCHPATHS(sp) {
00468 f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
00469 if (f != NULL || subdir == NO_DIRECTORY) break;
00470 }
00471
00472
00473 if (f == NULL && mode[0] == 'r' && subdir != NO_DIRECTORY) {
00474 static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1;
00475 char resolved_name[MAX_RESOLVED_LENGTH];
00476
00477
00478 strecpy(resolved_name, filename, lastof(resolved_name));
00479 strtolower(resolved_name);
00480
00481 size_t resolved_len = strlen(resolved_name);
00482
00483
00484 for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
00485 const std::string &src = link->first;
00486 size_t len = src.length();
00487 if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
00488
00489 char resolved_name2[MAX_RESOLVED_LENGTH];
00490 const std::string &dest = link->second;
00491 strecpy(resolved_name2, &(resolved_name[len]), lastof(resolved_name2));
00492 strecpy(resolved_name, dest.c_str(), lastof(resolved_name));
00493 strecpy(&(resolved_name[dest.length()]), resolved_name2, lastof(resolved_name));
00494 break;
00495 }
00496 }
00497
00498 TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
00499 if (it != _tar_filelist[subdir].end()) {
00500 f = FioFOpenFileTar(&((*it).second), filesize);
00501 }
00502 }
00503
00504
00505
00506 if (f == NULL && subdir != NO_DIRECTORY) {
00507 switch (subdir) {
00508 case BASESET_DIR:
00509 f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
00510 if (f != NULL) break;
00511
00512 case NEWGRF_DIR:
00513 f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
00514 break;
00515
00516 default:
00517 f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
00518 break;
00519 }
00520 }
00521
00522 return f;
00523 }
00524
00529 static void FioCreateDirectory(const char *name)
00530 {
00531 #if defined(WIN32) || defined(WINCE)
00532 CreateDirectory(OTTD2FS(name), NULL);
00533 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
00534 mkdir(OTTD2FS(name));
00535 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
00536 char buf[MAX_PATH];
00537 ttd_strlcpy(buf, name, MAX_PATH);
00538
00539 size_t len = strlen(name) - 1;
00540 if (buf[len] == '/') {
00541 buf[len] = '\0';
00542 }
00543
00544 mkdir(OTTD2FS(buf), 0755);
00545 #else
00546 mkdir(OTTD2FS(name), 0755);
00547 #endif
00548 }
00549
00557 bool AppendPathSeparator(char *buf, size_t buflen)
00558 {
00559 size_t s = strlen(buf);
00560
00561
00562 if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
00563 if (s + 2 >= buflen) return false;
00564
00565 buf[s] = PATHSEPCHAR;
00566 buf[s + 1] = '\0';
00567 }
00568
00569 return true;
00570 }
00571
00578 char *BuildWithFullPath(const char *dir)
00579 {
00580 char *dest = MallocT<char>(MAX_PATH);
00581 ttd_strlcpy(dest, dir, MAX_PATH);
00582
00583
00584 const char *s = strchr(dest, PATHSEPCHAR);
00585
00586
00587 if (s == NULL || dest != s) {
00588 if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0';
00589 AppendPathSeparator(dest, MAX_PATH);
00590 ttd_strlcat(dest, dir, MAX_PATH);
00591 }
00592 AppendPathSeparator(dest, MAX_PATH);
00593
00594 return dest;
00595 }
00596
00602 const char *FioTarFirstDir(const char *tarname, Subdirectory subdir)
00603 {
00604 TarList::iterator it = _tar_list[subdir].find(tarname);
00605 if (it == _tar_list[subdir].end()) return NULL;
00606 return (*it).second.dirname;
00607 }
00608
00609 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
00610 {
00611 std::string src = srcParam;
00612 std::string dest = destParam;
00613
00614 std::transform(src.begin(), src.end(), src.begin(), tolower);
00615 std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
00616
00617 TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
00618 if (dest_file != _tar_filelist[subdir].end()) {
00619
00620 _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
00621 } else {
00622
00623
00624 const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
00625 const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
00626 _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
00627 }
00628 }
00629
00630 void FioTarAddLink(const char *src, const char *dest, Subdirectory subdir)
00631 {
00632 TarAddLink(src, dest, subdir);
00633 }
00634
00640 static void SimplifyFileName(char *name)
00641 {
00642
00643 strtolower(name);
00644
00645
00646 #if (PATHSEPCHAR != '/')
00647 for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
00648 #endif
00649 }
00650
00656 uint TarScanner::DoScan(Subdirectory sd)
00657 {
00658 _tar_filelist[sd].clear();
00659 _tar_list[sd].clear();
00660 uint num = this->Scan(".tar", sd, false);
00661 if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
00662 return num;
00663 }
00664
00665 uint TarScanner::DoScan(TarScanner::Mode mode)
00666 {
00667 DEBUG(misc, 1, "Scanning for tars");
00668 TarScanner fs;
00669 uint num = 0;
00670 if (mode & TarScanner::BASESET) {
00671 num += fs.DoScan(BASESET_DIR);
00672 }
00673 if (mode & TarScanner::NEWGRF) {
00674 num += fs.DoScan(NEWGRF_DIR);
00675 }
00676 if (mode & TarScanner::AI) {
00677 num += fs.DoScan(AI_DIR);
00678 num += fs.DoScan(AI_LIBRARY_DIR);
00679 }
00680 if (mode & TarScanner::GAME) {
00681 num += fs.DoScan(GAME_DIR);
00682 num += fs.DoScan(GAME_LIBRARY_DIR);
00683 }
00684 if (mode & TarScanner::SCENARIO) {
00685 num += fs.DoScan(SCENARIO_DIR);
00686 num += fs.DoScan(HEIGHTMAP_DIR);
00687 }
00688 DEBUG(misc, 1, "Scan complete, found %d files", num);
00689 return num;
00690 }
00691
00698 bool TarScanner::AddFile(Subdirectory sd, const char *filename)
00699 {
00700 this->subdir = sd;
00701 return this->AddFile(filename, 0);
00702 }
00703
00704 bool TarScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00705 {
00706
00707 assert(tar_filename == NULL);
00708
00709
00710 typedef struct TarHeader {
00711 char name[100];
00712 char mode[8];
00713 char uid[8];
00714 char gid[8];
00715 char size[12];
00716 char mtime[12];
00717 char chksum[8];
00718 char typeflag;
00719 char linkname[100];
00720 char magic[6];
00721 char version[2];
00722 char uname[32];
00723 char gname[32];
00724 char devmajor[8];
00725 char devminor[8];
00726 char prefix[155];
00727
00728 char unused[12];
00729 } TarHeader;
00730
00731
00732 TarList::iterator it = _tar_list[this->subdir].find(filename);
00733 if (it != _tar_list[this->subdir].end()) return false;
00734
00735 FILE *f = fopen(filename, "rb");
00736
00737
00738
00739
00740 if (f == NULL) return false;
00741
00742 const char *dupped_filename = strdup(filename);
00743 _tar_list[this->subdir][filename].filename = dupped_filename;
00744 _tar_list[this->subdir][filename].dirname = NULL;
00745
00746 TarLinkList links;
00747
00748 TarHeader th;
00749 char buf[sizeof(th.name) + 1], *end;
00750 char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
00751 char link[sizeof(th.linkname) + 1];
00752 char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
00753 size_t num = 0, pos = 0;
00754
00755
00756 char empty[512];
00757 memset(&empty[0], 0, sizeof(empty));
00758
00759 for (;;) {
00760 size_t num_bytes_read = fread(&th, 1, 512, f);
00761 if (num_bytes_read != 512) break;
00762 pos += num_bytes_read;
00763
00764
00765 if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
00766
00767 if (memcmp(&th, &empty[0], 512) == 0) continue;
00768
00769 DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
00770 return false;
00771 }
00772
00773 name[0] = '\0';
00774 size_t len = 0;
00775
00776
00777 if (th.prefix[0] != '\0') {
00778 memcpy(name, th.prefix, sizeof(th.prefix));
00779 name[sizeof(th.prefix)] = '\0';
00780 len = strlen(name);
00781 name[len] = PATHSEPCHAR;
00782 len++;
00783 }
00784
00785
00786 memcpy(&name[len], th.name, sizeof(th.name));
00787 name[len + sizeof(th.name)] = '\0';
00788
00789
00790 memcpy(buf, th.size, sizeof(th.size));
00791 buf[sizeof(th.size)] = '\0';
00792 size_t skip = strtoul(buf, &end, 8);
00793
00794 switch (th.typeflag) {
00795 case '\0':
00796 case '0': {
00797
00798 if (skip == 0) break;
00799
00800 if (strlen(name) == 0) break;
00801
00802
00803 TarFileListEntry entry;
00804 entry.tar_filename = dupped_filename;
00805 entry.size = skip;
00806 entry.position = pos;
00807
00808
00809 SimplifyFileName(name);
00810
00811 DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
00812 if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
00813
00814 break;
00815 }
00816
00817 case '1':
00818 case '2': {
00819
00820 memcpy(link, th.linkname, sizeof(th.linkname));
00821 link[sizeof(th.linkname)] = '\0';
00822
00823 if (strlen(name) == 0 || strlen(link) == 0) break;
00824
00825
00826 SimplifyFileName(name);
00827 SimplifyFileName(link);
00828
00829
00830 if (link[0] == PATHSEPCHAR) {
00831 DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
00832 break;
00833 }
00834
00835
00836
00837 strecpy(dest, name, lastof(dest));
00838 char *destpos = strrchr(dest, PATHSEPCHAR);
00839 if (destpos == NULL) destpos = dest;
00840 *destpos = '\0';
00841
00842 char *pos = link;
00843 while (*pos != '\0') {
00844 char *next = strchr(link, PATHSEPCHAR);
00845 if (next == NULL) next = pos + strlen(pos);
00846
00847
00848 if (next != pos + 1 || pos[0] != '.') {
00849 if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') {
00850
00851 if (dest[0] == '\0') {
00852 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
00853 break;
00854 }
00855
00856
00857
00858 destpos = strrchr(dest, PATHSEPCHAR);
00859 if (destpos == NULL) destpos = dest;
00860 } else {
00861
00862 if (destpos != dest) *(destpos++) = PATHSEPCHAR;
00863 strncpy(destpos, pos, next - pos);
00864 destpos += next - pos;
00865 }
00866 *destpos = '\0';
00867 }
00868
00869 pos = next;
00870 }
00871
00872
00873 DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
00874 links.insert(TarLinkList::value_type(name, dest));
00875
00876 break;
00877 }
00878
00879 case '5':
00880
00881 SimplifyFileName(name);
00882
00883
00884 DEBUG(misc, 6, "Found dir in tar: %s", name);
00885 if (_tar_list[this->subdir][filename].dirname == NULL) _tar_list[this->subdir][filename].dirname = strdup(name);
00886 break;
00887
00888 default:
00889
00890 break;
00891 }
00892
00893
00894 skip = Align(skip, 512);
00895 fseek(f, skip, SEEK_CUR);
00896 pos += skip;
00897 }
00898
00899 DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
00900 fclose(f);
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
00912 const std::string &src = link->first;
00913 const std::string &dest = link->second;
00914 TarAddLink(src, dest, this->subdir);
00915 }
00916
00917 return true;
00918 }
00919
00927 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
00928 {
00929 TarList::iterator it = _tar_list[subdir].find(tar_filename);
00930
00931 if (it == _tar_list[subdir].end()) return false;
00932
00933 const char *dirname = (*it).second.dirname;
00934
00935
00936 if (dirname == NULL) return false;
00937
00938 char filename[MAX_PATH];
00939 strecpy(filename, tar_filename, lastof(filename));
00940 char *p = strrchr(filename, PATHSEPCHAR);
00941
00942 if (p == NULL) return false;
00943
00944 p++;
00945 strecpy(p, dirname, lastof(filename));
00946 DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
00947 FioCreateDirectory(filename);
00948
00949 for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
00950 if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
00951
00952 strecpy(p, (*it2).first.c_str(), lastof(filename));
00953
00954 DEBUG(misc, 9, " extracting %s", filename);
00955
00956
00957 size_t to_copy = 0;
00958 FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
00959 if (in == NULL) {
00960 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
00961 return false;
00962 }
00963
00964
00965 FILE *out = fopen(filename, "wb");
00966 if (out == NULL) {
00967 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
00968 fclose(in);
00969 return false;
00970 }
00971
00972
00973 char buffer[4096];
00974 size_t read;
00975 for (; to_copy != 0; to_copy -= read) {
00976 read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
00977 if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
00978 }
00979
00980
00981 fclose(in);
00982 fclose(out);
00983
00984 if (to_copy != 0) {
00985 DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
00986 return false;
00987 }
00988 }
00989
00990 DEBUG(misc, 9, " extraction successful");
00991 return true;
00992 }
00993
00994 #if defined(WIN32) || defined(WINCE)
00995
01000 extern void DetermineBasePaths(const char *exe);
01001 #else
01002
01010 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
01011 {
01012 bool success = false;
01013 #ifdef WITH_COCOA
01014 char *app_bundle = strchr(exe, '.');
01015 while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
01016
01017 if (app_bundle != NULL) app_bundle[0] = '\0';
01018 #endif
01019 char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
01020 if (s != NULL) {
01021 *s = '\0';
01022 #if defined(__DJGPP__)
01023
01024 if (s[-1] == ':') chdir("/");
01025 #endif
01026 if (chdir(exe) != 0) {
01027 DEBUG(misc, 0, "Directory with the binary does not exist?");
01028 } else {
01029 success = true;
01030 }
01031 *s = PATHSEPCHAR;
01032 }
01033 #ifdef WITH_COCOA
01034 if (app_bundle != NULL) app_bundle[0] = '.';
01035 #endif
01036 return success;
01037 }
01038
01049 bool DoScanWorkingDirectory()
01050 {
01051
01052 if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
01053
01054
01055 if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
01056
01057
01058 if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
01059
01060 char tmp[MAX_PATH];
01061 snprintf(tmp, lengthof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
01062 AppendPathSeparator(tmp, MAX_PATH);
01063 return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
01064 }
01065
01070 void DetermineBasePaths(const char *exe)
01071 {
01072 char tmp[MAX_PATH];
01073 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
01074 _searchpaths[SP_PERSONAL_DIR] = NULL;
01075 #else
01076 #ifdef __HAIKU__
01077 BPath path;
01078 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
01079 const char *homedir = path.Path();
01080 #else
01081 const char *homedir = getenv("HOME");
01082
01083 if (homedir == NULL) {
01084 const struct passwd *pw = getpwuid(getuid());
01085 homedir = (pw == NULL) ? "" : pw->pw_dir;
01086 }
01087 #endif
01088
01089 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
01090 AppendPathSeparator(tmp, MAX_PATH);
01091
01092 _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
01093 #endif
01094
01095 #if defined(WITH_SHARED_DIR)
01096 snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
01097 AppendPathSeparator(tmp, MAX_PATH);
01098 _searchpaths[SP_SHARED_DIR] = strdup(tmp);
01099 #else
01100 _searchpaths[SP_SHARED_DIR] = NULL;
01101 #endif
01102
01103 #if defined(__MORPHOS__) || defined(__AMIGA__)
01104 _searchpaths[SP_WORKING_DIR] = NULL;
01105 #else
01106 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01107 AppendPathSeparator(tmp, MAX_PATH);
01108 _searchpaths[SP_WORKING_DIR] = strdup(tmp);
01109 #endif
01110
01111 _do_scan_working_directory = DoScanWorkingDirectory();
01112
01113
01114 if (ChangeWorkingDirectoryToExecutable(exe)) {
01115 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01116 AppendPathSeparator(tmp, MAX_PATH);
01117 _searchpaths[SP_BINARY_DIR] = strdup(tmp);
01118 } else {
01119 _searchpaths[SP_BINARY_DIR] = NULL;
01120 }
01121
01122 if (_searchpaths[SP_WORKING_DIR] != NULL) {
01123
01124 if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
01125 DEBUG(misc, 0, "Failed to return to working directory!");
01126 }
01127 }
01128
01129 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
01130 _searchpaths[SP_INSTALLATION_DIR] = NULL;
01131 #else
01132 snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
01133 AppendPathSeparator(tmp, MAX_PATH);
01134 _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
01135 #endif
01136 #ifdef WITH_COCOA
01137 extern void cocoaSetApplicationBundleDir();
01138 cocoaSetApplicationBundleDir();
01139 #else
01140 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
01141 #endif
01142 }
01143 #endif
01144
01145 char *_personal_dir;
01146
01153 void DeterminePaths(const char *exe)
01154 {
01155 DetermineBasePaths(exe);
01156
01157 Searchpath sp;
01158 FOR_ALL_SEARCHPATHS(sp) {
01159 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01160 DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
01161 }
01162
01163 if (_config_file != NULL) {
01164 _personal_dir = strdup(_config_file);
01165 char *end = strrchr(_personal_dir, PATHSEPCHAR);
01166 if (end == NULL) {
01167 _personal_dir[0] = '\0';
01168 } else {
01169 end[1] = '\0';
01170 }
01171 } else {
01172 char personal_dir[MAX_PATH];
01173 if (FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg") != NULL) {
01174 char *end = strrchr(personal_dir, PATHSEPCHAR);
01175 if (end != NULL) end[1] = '\0';
01176 _personal_dir = strdup(personal_dir);
01177 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01178 } else {
01179 static const Searchpath new_openttd_cfg_order[] = {
01180 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
01181 };
01182
01183 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
01184 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
01185 _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
01186 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01187 break;
01188 }
01189 }
01190 }
01191 }
01192
01193 DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
01194
01195 _highscore_file = str_fmt("%shs.dat", _personal_dir);
01196 extern char *_hotkeys_file;
01197 _hotkeys_file = str_fmt("%shotkeys.cfg", _personal_dir);
01198
01199
01200 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
01201 FioCreateDirectory(_personal_dir);
01202 #endif
01203
01204 static const Subdirectory default_subdirs[] = {
01205 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR
01206 };
01207
01208 for (uint i = 0; i < lengthof(default_subdirs); i++) {
01209 char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
01210 FioCreateDirectory(dir);
01211 free(dir);
01212 }
01213
01214
01215 _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
01216 #ifdef ENABLE_NETWORK
01217 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01218
01219
01220 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };
01221 for (uint i = 0; i < lengthof(dirs); i++) {
01222 char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
01223 FioCreateDirectory(tmp);
01224 free(tmp);
01225 }
01226
01227 extern char *_log_file;
01228 _log_file = str_fmt("%sopenttd.log", _personal_dir);
01229 #else
01230
01231
01232 if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
01233 free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01234 _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
01235 }
01236 #endif
01237 }
01238
01243 void SanitizeFilename(char *filename)
01244 {
01245 for (; *filename != '\0'; filename++) {
01246 switch (*filename) {
01247
01248
01249 case ':': case '\\': case '*': case '?': case '/':
01250 case '<': case '>': case '|': case '"':
01251 *filename = '_';
01252 break;
01253 }
01254 }
01255 }
01256
01265 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
01266 {
01267 FILE *in = fopen(filename, "rb");
01268 if (in == NULL) return NULL;
01269
01270 fseek(in, 0, SEEK_END);
01271 size_t len = ftell(in);
01272 fseek(in, 0, SEEK_SET);
01273 if (len > maxsize) {
01274 fclose(in);
01275 return NULL;
01276 }
01277 byte *mem = MallocT<byte>(len + 1);
01278 mem[len] = 0;
01279 if (fread(mem, len, 1, in) != 1) {
01280 fclose(in);
01281 free(mem);
01282 return NULL;
01283 }
01284 fclose(in);
01285
01286 *lenp = len;
01287 return mem;
01288 }
01289
01296 static bool MatchesExtension(const char *extension, const char *filename)
01297 {
01298 if (extension == NULL) return true;
01299
01300 const char *ext = strrchr(filename, extension[0]);
01301 return ext != NULL && strcasecmp(ext, extension) == 0;
01302 }
01303
01313 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
01314 {
01315 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
01316
01317 uint num = 0;
01318 struct stat sb;
01319 struct dirent *dirent;
01320 DIR *dir;
01321
01322 if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
01323
01324 while ((dirent = readdir(dir)) != NULL) {
01325 const char *d_name = FS2OTTD(dirent->d_name);
01326 char filename[MAX_PATH];
01327
01328 if (!FiosIsValidFile(path, dirent, &sb)) continue;
01329
01330 snprintf(filename, lengthof(filename), "%s%s", path, d_name);
01331
01332 if (S_ISDIR(sb.st_mode)) {
01333
01334 if (!recursive) continue;
01335 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01336 if (!AppendPathSeparator(filename, lengthof(filename))) continue;
01337 num += ScanPath(fs, extension, filename, basepath_length, recursive);
01338 } else if (S_ISREG(sb.st_mode)) {
01339
01340 if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, NULL)) num++;
01341 }
01342 }
01343
01344 closedir(dir);
01345
01346 return num;
01347 }
01348
01355 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01356 {
01357 uint num = 0;
01358 const char *filename = (*tar).first.c_str();
01359
01360 if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
01361
01362 return num;
01363 }
01364
01374 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
01375 {
01376 this->subdir = sd;
01377
01378 Searchpath sp;
01379 char path[MAX_PATH];
01380 TarFileList::iterator tar;
01381 uint num = 0;
01382
01383 FOR_ALL_SEARCHPATHS(sp) {
01384
01385 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01386
01387 FioAppendDirectory(path, MAX_PATH, sp, sd);
01388 num += ScanPath(this, extension, path, strlen(path), recursive);
01389 }
01390
01391 if (tars && sd != NO_DIRECTORY) {
01392 FOR_ALL_TARS(tar, sd) {
01393 num += ScanTar(this, extension, tar);
01394 }
01395 }
01396
01397 switch (sd) {
01398 case BASESET_DIR:
01399 num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
01400
01401 case NEWGRF_DIR:
01402 num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
01403 break;
01404
01405 default: break;
01406 }
01407
01408 return num;
01409 }
01410
01419 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
01420 {
01421 char path[MAX_PATH];
01422 strecpy(path, directory, lastof(path));
01423 if (!AppendPathSeparator(path, lengthof(path))) return 0;
01424 return ScanPath(this, extension, path, strlen(path), recursive);
01425 }