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