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 }
00687 DEBUG(misc, 1, "Scan complete, found %d files", num);
00688 return num;
00689 }
00690
00697 bool TarScanner::AddFile(Subdirectory sd, const char *filename)
00698 {
00699 this->subdir = sd;
00700 return this->AddFile(filename, 0);
00701 }
00702
00703 bool TarScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00704 {
00705
00706 assert(tar_filename == NULL);
00707
00708
00709 typedef struct TarHeader {
00710 char name[100];
00711 char mode[8];
00712 char uid[8];
00713 char gid[8];
00714 char size[12];
00715 char mtime[12];
00716 char chksum[8];
00717 char typeflag;
00718 char linkname[100];
00719 char magic[6];
00720 char version[2];
00721 char uname[32];
00722 char gname[32];
00723 char devmajor[8];
00724 char devminor[8];
00725 char prefix[155];
00726
00727 char unused[12];
00728 } TarHeader;
00729
00730
00731 TarList::iterator it = _tar_list[this->subdir].find(filename);
00732 if (it != _tar_list[this->subdir].end()) return false;
00733
00734 FILE *f = fopen(filename, "rb");
00735
00736
00737
00738
00739 if (f == NULL) return false;
00740
00741 const char *dupped_filename = strdup(filename);
00742 _tar_list[this->subdir][filename].filename = dupped_filename;
00743 _tar_list[this->subdir][filename].dirname = NULL;
00744
00745 TarLinkList links;
00746
00747 TarHeader th;
00748 char buf[sizeof(th.name) + 1], *end;
00749 char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
00750 char link[sizeof(th.linkname) + 1];
00751 char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
00752 size_t num = 0, pos = 0;
00753
00754
00755 char empty[512];
00756 memset(&empty[0], 0, sizeof(empty));
00757
00758 for (;;) {
00759 size_t num_bytes_read = fread(&th, 1, 512, f);
00760 if (num_bytes_read != 512) break;
00761 pos += num_bytes_read;
00762
00763
00764 if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
00765
00766 if (memcmp(&th, &empty[0], 512) == 0) continue;
00767
00768 DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
00769 return false;
00770 }
00771
00772 name[0] = '\0';
00773 size_t len = 0;
00774
00775
00776 if (th.prefix[0] != '\0') {
00777 memcpy(name, th.prefix, sizeof(th.prefix));
00778 name[sizeof(th.prefix)] = '\0';
00779 len = strlen(name);
00780 name[len] = PATHSEPCHAR;
00781 len++;
00782 }
00783
00784
00785 memcpy(&name[len], th.name, sizeof(th.name));
00786 name[len + sizeof(th.name)] = '\0';
00787
00788
00789 memcpy(buf, th.size, sizeof(th.size));
00790 buf[sizeof(th.size)] = '\0';
00791 size_t skip = strtoul(buf, &end, 8);
00792
00793 switch (th.typeflag) {
00794 case '\0':
00795 case '0': {
00796
00797 if (skip == 0) break;
00798
00799 if (strlen(name) == 0) break;
00800
00801
00802 TarFileListEntry entry;
00803 entry.tar_filename = dupped_filename;
00804 entry.size = skip;
00805 entry.position = pos;
00806
00807
00808 SimplifyFileName(name);
00809
00810 DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
00811 if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
00812
00813 break;
00814 }
00815
00816 case '1':
00817 case '2': {
00818
00819 memcpy(link, th.linkname, sizeof(th.linkname));
00820 link[sizeof(th.linkname)] = '\0';
00821
00822 if (strlen(name) == 0 || strlen(link) == 0) break;
00823
00824
00825 SimplifyFileName(name);
00826 SimplifyFileName(link);
00827
00828
00829 if (link[0] == PATHSEPCHAR) {
00830 DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
00831 break;
00832 }
00833
00834
00835
00836 strecpy(dest, name, lastof(dest));
00837 char *destpos = strrchr(dest, PATHSEPCHAR);
00838 if (destpos == NULL) destpos = dest;
00839 *destpos = '\0';
00840
00841 char *pos = link;
00842 while (*pos != '\0') {
00843 char *next = strchr(link, PATHSEPCHAR);
00844 if (next == NULL) next = pos + strlen(pos);
00845
00846
00847 if (next != pos + 1 || pos[0] != '.') {
00848 if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') {
00849
00850 if (dest[0] == '\0') {
00851 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
00852 break;
00853 }
00854
00855
00856
00857 destpos = strrchr(dest, PATHSEPCHAR);
00858 if (destpos == NULL) destpos = dest;
00859 } else {
00860
00861 if (destpos != dest) *(destpos++) = PATHSEPCHAR;
00862 strncpy(destpos, pos, next - pos);
00863 destpos += next - pos;
00864 }
00865 *destpos = '\0';
00866 }
00867
00868 pos = next;
00869 }
00870
00871
00872 DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
00873 links.insert(TarLinkList::value_type(name, dest));
00874
00875 break;
00876 }
00877
00878 case '5':
00879
00880 SimplifyFileName(name);
00881
00882
00883 DEBUG(misc, 6, "Found dir in tar: %s", name);
00884 if (_tar_list[this->subdir][filename].dirname == NULL) _tar_list[this->subdir][filename].dirname = strdup(name);
00885 break;
00886
00887 default:
00888
00889 break;
00890 }
00891
00892
00893 skip = Align(skip, 512);
00894 fseek(f, skip, SEEK_CUR);
00895 pos += skip;
00896 }
00897
00898 DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
00899 fclose(f);
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
00911 const std::string &src = link->first;
00912 const std::string &dest = link->second;
00913 TarAddLink(src, dest, this->subdir);
00914 }
00915
00916 return true;
00917 }
00918
00926 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
00927 {
00928 TarList::iterator it = _tar_list[subdir].find(tar_filename);
00929
00930 if (it == _tar_list[subdir].end()) return false;
00931
00932 const char *dirname = (*it).second.dirname;
00933
00934
00935 if (dirname == NULL) return false;
00936
00937 char filename[MAX_PATH];
00938 strecpy(filename, tar_filename, lastof(filename));
00939 char *p = strrchr(filename, PATHSEPCHAR);
00940
00941 if (p == NULL) return false;
00942
00943 p++;
00944 strecpy(p, dirname, lastof(filename));
00945 DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
00946 FioCreateDirectory(filename);
00947
00948 for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
00949 if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
00950
00951 strecpy(p, (*it2).first.c_str(), lastof(filename));
00952
00953 DEBUG(misc, 9, " extracting %s", filename);
00954
00955
00956 size_t to_copy = 0;
00957 FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
00958 if (in == NULL) {
00959 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
00960 return false;
00961 }
00962
00963
00964 FILE *out = fopen(filename, "wb");
00965 if (out == NULL) {
00966 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
00967 fclose(in);
00968 return false;
00969 }
00970
00971
00972 char buffer[4096];
00973 size_t read;
00974 for (; to_copy != 0; to_copy -= read) {
00975 read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
00976 if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
00977 }
00978
00979
00980 fclose(in);
00981 fclose(out);
00982
00983 if (to_copy != 0) {
00984 DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
00985 return false;
00986 }
00987 }
00988
00989 DEBUG(misc, 9, " extraction successful");
00990 return true;
00991 }
00992
00993 #if defined(WIN32) || defined(WINCE)
00994
00999 extern void DetermineBasePaths(const char *exe);
01000 #else
01001
01009 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
01010 {
01011 bool success = false;
01012 #ifdef WITH_COCOA
01013 char *app_bundle = strchr(exe, '.');
01014 while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
01015
01016 if (app_bundle != NULL) app_bundle[0] = '\0';
01017 #endif
01018 char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
01019 if (s != NULL) {
01020 *s = '\0';
01021 #if defined(__DJGPP__)
01022
01023 if (s[-1] == ':') chdir("/");
01024 #endif
01025 if (chdir(exe) != 0) {
01026 DEBUG(misc, 0, "Directory with the binary does not exist?");
01027 } else {
01028 success = true;
01029 }
01030 *s = PATHSEPCHAR;
01031 }
01032 #ifdef WITH_COCOA
01033 if (app_bundle != NULL) app_bundle[0] = '.';
01034 #endif
01035 return success;
01036 }
01037
01048 bool DoScanWorkingDirectory()
01049 {
01050
01051 if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
01052
01053
01054 if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
01055
01056
01057 if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
01058
01059 char tmp[MAX_PATH];
01060 snprintf(tmp, lengthof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
01061 AppendPathSeparator(tmp, MAX_PATH);
01062 return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
01063 }
01064
01069 void DetermineBasePaths(const char *exe)
01070 {
01071 char tmp[MAX_PATH];
01072 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
01073 _searchpaths[SP_PERSONAL_DIR] = NULL;
01074 #else
01075 #ifdef __HAIKU__
01076 BPath path;
01077 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
01078 const char *homedir = path.Path();
01079 #else
01080 const char *homedir = getenv("HOME");
01081
01082 if (homedir == NULL) {
01083 const struct passwd *pw = getpwuid(getuid());
01084 homedir = (pw == NULL) ? "" : pw->pw_dir;
01085 }
01086 #endif
01087
01088 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
01089 AppendPathSeparator(tmp, MAX_PATH);
01090
01091 _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
01092 #endif
01093
01094 #if defined(WITH_SHARED_DIR)
01095 snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
01096 AppendPathSeparator(tmp, MAX_PATH);
01097 _searchpaths[SP_SHARED_DIR] = strdup(tmp);
01098 #else
01099 _searchpaths[SP_SHARED_DIR] = NULL;
01100 #endif
01101
01102 #if defined(__MORPHOS__) || defined(__AMIGA__)
01103 _searchpaths[SP_WORKING_DIR] = NULL;
01104 #else
01105 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01106 AppendPathSeparator(tmp, MAX_PATH);
01107 _searchpaths[SP_WORKING_DIR] = strdup(tmp);
01108 #endif
01109
01110 _do_scan_working_directory = DoScanWorkingDirectory();
01111
01112
01113 if (ChangeWorkingDirectoryToExecutable(exe)) {
01114 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01115 AppendPathSeparator(tmp, MAX_PATH);
01116 _searchpaths[SP_BINARY_DIR] = strdup(tmp);
01117 } else {
01118 _searchpaths[SP_BINARY_DIR] = NULL;
01119 }
01120
01121 if (_searchpaths[SP_WORKING_DIR] != NULL) {
01122
01123 if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
01124 DEBUG(misc, 0, "Failed to return to working directory!");
01125 }
01126 }
01127
01128 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
01129 _searchpaths[SP_INSTALLATION_DIR] = NULL;
01130 #else
01131 snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
01132 AppendPathSeparator(tmp, MAX_PATH);
01133 _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
01134 #endif
01135 #ifdef WITH_COCOA
01136 extern void cocoaSetApplicationBundleDir();
01137 cocoaSetApplicationBundleDir();
01138 #else
01139 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
01140 #endif
01141 }
01142 #endif
01143
01144 char *_personal_dir;
01145
01152 void DeterminePaths(const char *exe)
01153 {
01154 DetermineBasePaths(exe);
01155
01156 Searchpath sp;
01157 FOR_ALL_SEARCHPATHS(sp) {
01158 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01159 DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
01160 }
01161
01162 if (_config_file != NULL) {
01163 _personal_dir = strdup(_config_file);
01164 char *end = strrchr(_personal_dir, PATHSEPCHAR);
01165 if (end == NULL) {
01166 _personal_dir[0] = '\0';
01167 } else {
01168 end[1] = '\0';
01169 }
01170 } else {
01171 char personal_dir[MAX_PATH];
01172 if (FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg") != NULL) {
01173 char *end = strrchr(personal_dir, PATHSEPCHAR);
01174 if (end != NULL) end[1] = '\0';
01175 _personal_dir = strdup(personal_dir);
01176 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01177 } else {
01178 static const Searchpath new_openttd_cfg_order[] = {
01179 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
01180 };
01181
01182 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
01183 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
01184 _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
01185 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01186 break;
01187 }
01188 }
01189 }
01190 }
01191
01192 DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
01193
01194 _highscore_file = str_fmt("%shs.dat", _personal_dir);
01195 extern char *_hotkeys_file;
01196 _hotkeys_file = str_fmt("%shotkeys.cfg", _personal_dir);
01197
01198
01199 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
01200 FioCreateDirectory(_personal_dir);
01201 #endif
01202
01203 static const Subdirectory default_subdirs[] = {
01204 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR
01205 };
01206
01207 for (uint i = 0; i < lengthof(default_subdirs); i++) {
01208 char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
01209 FioCreateDirectory(dir);
01210 free(dir);
01211 }
01212
01213
01214 _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
01215 #ifdef ENABLE_NETWORK
01216 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01217
01218
01219 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };
01220 for (uint i = 0; i < lengthof(dirs); i++) {
01221 char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
01222 FioCreateDirectory(tmp);
01223 free(tmp);
01224 }
01225
01226 extern char *_log_file;
01227 _log_file = str_fmt("%sopenttd.log", _personal_dir);
01228 #else
01229
01230
01231 if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
01232 free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01233 _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
01234 }
01235 #endif
01236 }
01237
01242 void SanitizeFilename(char *filename)
01243 {
01244 for (; *filename != '\0'; filename++) {
01245 switch (*filename) {
01246
01247
01248 case ':': case '\\': case '*': case '?': case '/':
01249 case '<': case '>': case '|': case '"':
01250 *filename = '_';
01251 break;
01252 }
01253 }
01254 }
01255
01264 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
01265 {
01266 FILE *in = fopen(filename, "rb");
01267 if (in == NULL) return NULL;
01268
01269 fseek(in, 0, SEEK_END);
01270 size_t len = ftell(in);
01271 fseek(in, 0, SEEK_SET);
01272 if (len > maxsize) {
01273 fclose(in);
01274 return NULL;
01275 }
01276 byte *mem = MallocT<byte>(len + 1);
01277 mem[len] = 0;
01278 if (fread(mem, len, 1, in) != 1) {
01279 fclose(in);
01280 free(mem);
01281 return NULL;
01282 }
01283 fclose(in);
01284
01285 *lenp = len;
01286 return mem;
01287 }
01288
01295 static bool MatchesExtension(const char *extension, const char *filename)
01296 {
01297 if (extension == NULL) return true;
01298
01299 const char *ext = strrchr(filename, extension[0]);
01300 return ext != NULL && strcasecmp(ext, extension) == 0;
01301 }
01302
01312 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
01313 {
01314 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
01315
01316 uint num = 0;
01317 struct stat sb;
01318 struct dirent *dirent;
01319 DIR *dir;
01320
01321 if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
01322
01323 while ((dirent = readdir(dir)) != NULL) {
01324 const char *d_name = FS2OTTD(dirent->d_name);
01325 char filename[MAX_PATH];
01326
01327 if (!FiosIsValidFile(path, dirent, &sb)) continue;
01328
01329 snprintf(filename, lengthof(filename), "%s%s", path, d_name);
01330
01331 if (S_ISDIR(sb.st_mode)) {
01332
01333 if (!recursive) continue;
01334 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01335 if (!AppendPathSeparator(filename, lengthof(filename))) continue;
01336 num += ScanPath(fs, extension, filename, basepath_length, recursive);
01337 } else if (S_ISREG(sb.st_mode)) {
01338
01339 if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, NULL)) num++;
01340 }
01341 }
01342
01343 closedir(dir);
01344
01345 return num;
01346 }
01347
01354 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01355 {
01356 uint num = 0;
01357 const char *filename = (*tar).first.c_str();
01358
01359 if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
01360
01361 return num;
01362 }
01363
01373 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
01374 {
01375 this->subdir = sd;
01376
01377 Searchpath sp;
01378 char path[MAX_PATH];
01379 TarFileList::iterator tar;
01380 uint num = 0;
01381
01382 FOR_ALL_SEARCHPATHS(sp) {
01383
01384 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01385
01386 FioAppendDirectory(path, MAX_PATH, sp, sd);
01387 num += ScanPath(this, extension, path, strlen(path), recursive);
01388 }
01389
01390 if (tars && sd != NO_DIRECTORY) {
01391 FOR_ALL_TARS(tar, sd) {
01392 num += ScanTar(this, extension, tar);
01393 }
01394 }
01395
01396 switch (sd) {
01397 case BASESET_DIR:
01398 num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
01399
01400 case NEWGRF_DIR:
01401 num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
01402 break;
01403
01404 default: break;
01405 }
01406
01407 return num;
01408 }
01409
01418 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
01419 {
01420 char path[MAX_PATH];
01421 strecpy(path, directory, lastof(path));
01422 if (!AppendPathSeparator(path, lengthof(path))) return 0;
01423 return ScanPath(this, extension, path, strlen(path), recursive);
01424 }