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 extern char *_windows_file;
01200 _windows_file = str_fmt("%swindows.cfg", _personal_dir);
01201
01202
01203 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
01204 FioCreateDirectory(_personal_dir);
01205 #endif
01206
01207 static const Subdirectory default_subdirs[] = {
01208 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR
01209 };
01210
01211 for (uint i = 0; i < lengthof(default_subdirs); i++) {
01212 char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
01213 FioCreateDirectory(dir);
01214 free(dir);
01215 }
01216
01217
01218 _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
01219 #ifdef ENABLE_NETWORK
01220 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01221
01222
01223 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };
01224 for (uint i = 0; i < lengthof(dirs); i++) {
01225 char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
01226 FioCreateDirectory(tmp);
01227 free(tmp);
01228 }
01229
01230 extern char *_log_file;
01231 _log_file = str_fmt("%sopenttd.log", _personal_dir);
01232 #else
01233
01234
01235 if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
01236 free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01237 _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
01238 }
01239 #endif
01240 }
01241
01246 void SanitizeFilename(char *filename)
01247 {
01248 for (; *filename != '\0'; filename++) {
01249 switch (*filename) {
01250
01251
01252 case ':': case '\\': case '*': case '?': case '/':
01253 case '<': case '>': case '|': case '"':
01254 *filename = '_';
01255 break;
01256 }
01257 }
01258 }
01259
01268 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
01269 {
01270 FILE *in = fopen(filename, "rb");
01271 if (in == NULL) return NULL;
01272
01273 fseek(in, 0, SEEK_END);
01274 size_t len = ftell(in);
01275 fseek(in, 0, SEEK_SET);
01276 if (len > maxsize) {
01277 fclose(in);
01278 return NULL;
01279 }
01280 byte *mem = MallocT<byte>(len + 1);
01281 mem[len] = 0;
01282 if (fread(mem, len, 1, in) != 1) {
01283 fclose(in);
01284 free(mem);
01285 return NULL;
01286 }
01287 fclose(in);
01288
01289 *lenp = len;
01290 return mem;
01291 }
01292
01299 static bool MatchesExtension(const char *extension, const char *filename)
01300 {
01301 if (extension == NULL) return true;
01302
01303 const char *ext = strrchr(filename, extension[0]);
01304 return ext != NULL && strcasecmp(ext, extension) == 0;
01305 }
01306
01316 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
01317 {
01318 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
01319
01320 uint num = 0;
01321 struct stat sb;
01322 struct dirent *dirent;
01323 DIR *dir;
01324
01325 if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
01326
01327 while ((dirent = readdir(dir)) != NULL) {
01328 const char *d_name = FS2OTTD(dirent->d_name);
01329 char filename[MAX_PATH];
01330
01331 if (!FiosIsValidFile(path, dirent, &sb)) continue;
01332
01333 snprintf(filename, lengthof(filename), "%s%s", path, d_name);
01334
01335 if (S_ISDIR(sb.st_mode)) {
01336
01337 if (!recursive) continue;
01338 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01339 if (!AppendPathSeparator(filename, lengthof(filename))) continue;
01340 num += ScanPath(fs, extension, filename, basepath_length, recursive);
01341 } else if (S_ISREG(sb.st_mode)) {
01342
01343 if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, NULL)) num++;
01344 }
01345 }
01346
01347 closedir(dir);
01348
01349 return num;
01350 }
01351
01358 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01359 {
01360 uint num = 0;
01361 const char *filename = (*tar).first.c_str();
01362
01363 if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
01364
01365 return num;
01366 }
01367
01377 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
01378 {
01379 this->subdir = sd;
01380
01381 Searchpath sp;
01382 char path[MAX_PATH];
01383 TarFileList::iterator tar;
01384 uint num = 0;
01385
01386 FOR_ALL_SEARCHPATHS(sp) {
01387
01388 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01389
01390 FioAppendDirectory(path, MAX_PATH, sp, sd);
01391 num += ScanPath(this, extension, path, strlen(path), recursive);
01392 }
01393
01394 if (tars && sd != NO_DIRECTORY) {
01395 FOR_ALL_TARS(tar, sd) {
01396 num += ScanTar(this, extension, tar);
01397 }
01398 }
01399
01400 switch (sd) {
01401 case BASESET_DIR:
01402 num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
01403
01404 case NEWGRF_DIR:
01405 num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
01406 break;
01407
01408 default: break;
01409 }
01410
01411 return num;
01412 }
01413
01422 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
01423 {
01424 char path[MAX_PATH];
01425 strecpy(path, directory, lastof(path));
01426 if (!AppendPathSeparator(path, lengthof(path))) return 0;
01427 return ScanPath(this, extension, path, strlen(path), recursive);
01428 }