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 #elif defined(__HAIKU__)
00021 #include <Path.h>
00022 #include <storage/FindDirectory.h>
00023 #else
00024 #if defined(OPENBSD) || defined(DOS)
00025 #include <unistd.h>
00026 #endif
00027 #include <pwd.h>
00028 #endif
00029 #include <sys/stat.h>
00030 #include <algorithm>
00031
00032
00033
00034
00035
00037 #define FIO_BUFFER_SIZE 512
00038
00040 struct Fio {
00041 byte *buffer, *buffer_end;
00042 size_t pos;
00043 FILE *cur_fh;
00044 const char *filename;
00045 FILE *handles[MAX_FILE_SLOTS];
00046 byte buffer_start[FIO_BUFFER_SIZE];
00047 const char *filenames[MAX_FILE_SLOTS];
00048 char *shortnames[MAX_FILE_SLOTS];
00049 #if defined(LIMITED_FDS)
00050 uint open_handles;
00051 uint usage_count[MAX_FILE_SLOTS];
00052 #endif
00053 };
00054
00055 static Fio _fio;
00056
00058 static bool _do_scan_working_directory = true;
00059
00060 extern char *_config_file;
00061 extern char *_highscore_file;
00062
00064 size_t FioGetPos()
00065 {
00066 return _fio.pos + (_fio.buffer - _fio.buffer_end);
00067 }
00068
00074 const char *FioGetFilename(uint8 slot)
00075 {
00076 return _fio.shortnames[slot];
00077 }
00078
00079 void FioSeekTo(size_t pos, int mode)
00080 {
00081 if (mode == SEEK_CUR) pos += FioGetPos();
00082 _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
00083 _fio.pos = pos;
00084 fseek(_fio.cur_fh, _fio.pos, SEEK_SET);
00085 }
00086
00087 #if defined(LIMITED_FDS)
00088 static void FioRestoreFile(int slot)
00089 {
00090
00091 if (_fio.handles[slot] == NULL) {
00092 DEBUG(misc, 6, "Restoring file '%s' in slot '%d' from disk", _fio.filenames[slot], slot);
00093 FioOpenFile(slot, _fio.filenames[slot]);
00094 }
00095 _fio.usage_count[slot]++;
00096 }
00097 #endif
00098
00099
00100 void FioSeekToFile(uint8 slot, size_t pos)
00101 {
00102 FILE *f;
00103 #if defined(LIMITED_FDS)
00104
00105 FioRestoreFile(slot);
00106 #endif
00107 f = _fio.handles[slot];
00108 assert(f != NULL);
00109 _fio.cur_fh = f;
00110 _fio.filename = _fio.filenames[slot];
00111 FioSeekTo(pos, SEEK_SET);
00112 }
00113
00114 byte FioReadByte()
00115 {
00116 if (_fio.buffer == _fio.buffer_end) {
00117 _fio.buffer = _fio.buffer_start;
00118 size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
00119 _fio.pos += size;
00120 _fio.buffer_end = _fio.buffer_start + size;
00121
00122 if (size == 0) return 0;
00123 }
00124 return *_fio.buffer++;
00125 }
00126
00127 void FioSkipBytes(int n)
00128 {
00129 for (;;) {
00130 int m = min(_fio.buffer_end - _fio.buffer, n);
00131 _fio.buffer += m;
00132 n -= m;
00133 if (n == 0) break;
00134 FioReadByte();
00135 n--;
00136 }
00137 }
00138
00139 uint16 FioReadWord()
00140 {
00141 byte b = FioReadByte();
00142 return (FioReadByte() << 8) | b;
00143 }
00144
00145 uint32 FioReadDword()
00146 {
00147 uint b = FioReadWord();
00148 return (FioReadWord() << 16) | b;
00149 }
00150
00151 void FioReadBlock(void *ptr, size_t size)
00152 {
00153 FioSeekTo(FioGetPos(), SEEK_SET);
00154 _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
00155 }
00156
00157 static inline void FioCloseFile(int slot)
00158 {
00159 if (_fio.handles[slot] != NULL) {
00160 fclose(_fio.handles[slot]);
00161
00162 free(_fio.shortnames[slot]);
00163 _fio.shortnames[slot] = NULL;
00164
00165 _fio.handles[slot] = NULL;
00166 #if defined(LIMITED_FDS)
00167 _fio.open_handles--;
00168 #endif
00169 }
00170 }
00171
00172 void FioCloseAll()
00173 {
00174 for (int i = 0; i != lengthof(_fio.handles); i++) {
00175 FioCloseFile(i);
00176 }
00177 }
00178
00179 #if defined(LIMITED_FDS)
00180 static void FioFreeHandle()
00181 {
00182
00183 if (_fio.open_handles + 1 == LIMITED_FDS) {
00184 uint i, count;
00185 int slot;
00186
00187 count = UINT_MAX;
00188 slot = -1;
00189
00190 for (i = 0; i < lengthof(_fio.handles); i++) {
00191 if (_fio.handles[i] != NULL && _fio.usage_count[i] < count) {
00192 count = _fio.usage_count[i];
00193 slot = i;
00194 }
00195 }
00196 assert(slot != -1);
00197 DEBUG(misc, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.filenames[slot], slot);
00198 FioCloseFile(slot);
00199 }
00200 }
00201 #endif
00202
00203 void FioOpenFile(int slot, const char *filename)
00204 {
00205 FILE *f;
00206
00207 #if defined(LIMITED_FDS)
00208 FioFreeHandle();
00209 #endif
00210 f = FioFOpenFile(filename);
00211 if (f == NULL) usererror("Cannot open file '%s'", filename);
00212 uint32 pos = ftell(f);
00213
00214 FioCloseFile(slot);
00215 _fio.handles[slot] = f;
00216 _fio.filenames[slot] = filename;
00217
00218
00219 const char *t = strrchr(filename, PATHSEPCHAR);
00220 _fio.shortnames[slot] = strdup(t == NULL ? filename : t);
00221 char *t2 = strrchr(_fio.shortnames[slot], '.');
00222 if (t2 != NULL) *t2 = '\0';
00223 strtolower(_fio.shortnames[slot]);
00224
00225 #if defined(LIMITED_FDS)
00226 _fio.usage_count[slot] = 0;
00227 _fio.open_handles++;
00228 #endif
00229 FioSeekToFile(slot, pos);
00230 }
00231
00232 static const char * const _subdirs[NUM_SUBDIRS] = {
00233 "",
00234 "save" PATHSEP,
00235 "save" PATHSEP "autosave" PATHSEP,
00236 "scenario" PATHSEP,
00237 "scenario" PATHSEP "heightmap" PATHSEP,
00238 "gm" PATHSEP,
00239 "data" PATHSEP,
00240 "lang" PATHSEP,
00241 "ai" PATHSEP,
00242 "ai" PATHSEP "library" PATHSEP,
00243 };
00244
00245 const char *_searchpaths[NUM_SEARCHPATHS];
00246 TarList _tar_list;
00247 TarFileList _tar_filelist;
00248
00249 typedef std::map<std::string, std::string> TarLinkList;
00250 static TarLinkList _tar_linklist;
00251
00258 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
00259 {
00260 FILE *f = FioFOpenFile(filename, "rb", subdir);
00261 if (f == NULL) return false;
00262
00263 FioFCloseFile(f);
00264 return true;
00265 }
00266
00270 void FioFCloseFile(FILE *f)
00271 {
00272 fclose(f);
00273 }
00274
00275 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
00276 {
00277 assert(subdir < NUM_SUBDIRS);
00278 assert(sp < NUM_SEARCHPATHS);
00279
00280 snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00281 return buf;
00282 }
00283
00284 char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
00285 {
00286 Searchpath sp;
00287 assert(subdir < NUM_SUBDIRS);
00288
00289 FOR_ALL_SEARCHPATHS(sp) {
00290 FioGetFullPath(buf, buflen, sp, subdir, filename);
00291 if (FileExists(buf)) break;
00292 #if !defined(WIN32)
00293
00294
00295
00296 strtolower(buf + strlen(_searchpaths[sp]) - 1);
00297 if (FileExists(buf)) break;
00298 #endif
00299 }
00300
00301 return buf;
00302 }
00303
00304 char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
00305 {
00306 assert(subdir < NUM_SUBDIRS);
00307 assert(sp < NUM_SEARCHPATHS);
00308
00309 snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
00310 return buf;
00311 }
00312
00313 char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
00314 {
00315 Searchpath sp;
00316
00317
00318 FOR_ALL_SEARCHPATHS(sp) {
00319 char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
00320 if (FileExists(buf)) return ret;
00321 }
00322
00323
00324 ttd_strlcpy(buf, _personal_dir, buflen);
00325
00326 return buf;
00327 }
00328
00329 static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
00330 {
00331 #if defined(WIN32) && defined(UNICODE)
00332
00333
00334
00335
00336 wchar_t Lmode[5];
00337 MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
00338 #endif
00339 FILE *f = NULL;
00340 char buf[MAX_PATH];
00341
00342 if (subdir == NO_DIRECTORY) {
00343 strecpy(buf, filename, lastof(buf));
00344 } else {
00345 snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00346 }
00347
00348 #if defined(WIN32)
00349 if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
00350 #endif
00351
00352 f = fopen(buf, mode);
00353 #if !defined(WIN32)
00354 if (f == NULL) {
00355 strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1));
00356 f = fopen(buf, mode);
00357 }
00358 #endif
00359 if (f != NULL && filesize != NULL) {
00360
00361 fseek(f, 0, SEEK_END);
00362 *filesize = ftell(f);
00363 fseek(f, 0, SEEK_SET);
00364 }
00365 return f;
00366 }
00367
00368 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
00369 {
00370 FILE *f = fopen(entry->tar_filename, "rb");
00371 if (f == NULL) return f;
00372
00373 fseek(f, entry->position, SEEK_SET);
00374 if (filesize != NULL) *filesize = entry->size;
00375 return f;
00376 }
00377
00379 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
00380 {
00381 FILE *f = NULL;
00382 Searchpath sp;
00383
00384 assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
00385
00386 FOR_ALL_SEARCHPATHS(sp) {
00387 f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
00388 if (f != NULL || subdir == NO_DIRECTORY) break;
00389 }
00390
00391
00392 if (f == NULL && mode[0] == 'r') {
00393 static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1;
00394 char resolved_name[MAX_RESOLVED_LENGTH];
00395
00396
00397 strecpy(resolved_name, filename, lastof(resolved_name));
00398 strtolower(resolved_name);
00399
00400 size_t resolved_len = strlen(resolved_name);
00401
00402
00403 for (TarLinkList::iterator link = _tar_linklist.begin(); link != _tar_linklist.end(); link++) {
00404 const std::string &src = link->first;
00405 size_t len = src.length();
00406 if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
00407
00408 char resolved_name2[MAX_RESOLVED_LENGTH];
00409 const std::string &dest = link->second;
00410 strecpy(resolved_name2, &(resolved_name[len]), lastof(resolved_name2));
00411 strecpy(resolved_name, dest.c_str(), lastof(resolved_name));
00412 strecpy(&(resolved_name[dest.length()]), resolved_name2, lastof(resolved_name));
00413 break;
00414 }
00415 }
00416
00417 TarFileList::iterator it = _tar_filelist.find(resolved_name);
00418 if (it != _tar_filelist.end()) {
00419 f = FioFOpenFileTar(&((*it).second), filesize);
00420 }
00421 }
00422
00423
00424
00425 if (f == NULL && subdir != NO_DIRECTORY) {
00426 f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
00427 }
00428
00429 return f;
00430 }
00431
00436 static void FioCreateDirectory(const char *name)
00437 {
00438 #if defined(WIN32) || defined(WINCE)
00439 CreateDirectory(OTTD2FS(name), NULL);
00440 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
00441 mkdir(OTTD2FS(name));
00442 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
00443 char buf[MAX_PATH];
00444 ttd_strlcpy(buf, name, MAX_PATH);
00445
00446 size_t len = strlen(name) - 1;
00447 if (buf[len] == '/') {
00448 buf[len] = '\0';
00449 }
00450
00451 mkdir(OTTD2FS(buf), 0755);
00452 #else
00453 mkdir(OTTD2FS(name), 0755);
00454 #endif
00455 }
00456
00464 bool AppendPathSeparator(char *buf, size_t buflen)
00465 {
00466 size_t s = strlen(buf);
00467
00468
00469 if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
00470 if (s + 2 >= buflen) return false;
00471
00472 buf[s] = PATHSEPCHAR;
00473 buf[s + 1] = '\0';
00474 }
00475
00476 return true;
00477 }
00478
00485 char *BuildWithFullPath(const char *dir)
00486 {
00487 char *dest = MallocT<char>(MAX_PATH);
00488 ttd_strlcpy(dest, dir, MAX_PATH);
00489
00490
00491 const char *s = strchr(dest, PATHSEPCHAR);
00492
00493
00494 if (s == NULL || dest != s) {
00495 if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0';
00496 AppendPathSeparator(dest, MAX_PATH);
00497 ttd_strlcat(dest, dir, MAX_PATH);
00498 }
00499 AppendPathSeparator(dest, MAX_PATH);
00500
00501 return dest;
00502 }
00503
00504 const char *FioTarFirstDir(const char *tarname)
00505 {
00506 TarList::iterator it = _tar_list.find(tarname);
00507 if (it == _tar_list.end()) return NULL;
00508 return (*it).second.dirname;
00509 }
00510
00511 static void TarAddLink(const std::string &srcParam, const std::string &destParam)
00512 {
00513 std::string src = srcParam;
00514 std::string dest = destParam;
00515
00516 std::transform(src.begin(), src.end(), src.begin(), tolower);
00517 std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
00518
00519 TarFileList::iterator dest_file = _tar_filelist.find(dest);
00520 if (dest_file != _tar_filelist.end()) {
00521
00522 _tar_filelist.insert(TarFileList::value_type(src, dest_file->second));
00523 } else {
00524
00525
00526 const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
00527 const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
00528 _tar_linklist.insert(TarLinkList::value_type(src_path, dst_path));
00529 }
00530 }
00531
00532 void FioTarAddLink(const char *src, const char *dest)
00533 {
00534 TarAddLink(src, dest);
00535 }
00536
00542 static void SimplifyFileName(char *name)
00543 {
00544
00545 strtolower(name);
00546
00547
00548 #if (PATHSEPCHAR != '/')
00549 for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
00550 #endif
00551 }
00552
00553 uint TarScanner::DoScan()
00554 {
00555 _tar_filelist.clear();
00556 _tar_list.clear();
00557
00558 DEBUG(misc, 1, "Scanning for tars");
00559 TarScanner fs;
00560 uint num = fs.Scan(".tar", DATA_DIR, false);
00561 num += fs.Scan(".tar", AI_DIR, false);
00562 num += fs.Scan(".tar", AI_LIBRARY_DIR, false);
00563 num += fs.Scan(".tar", SCENARIO_DIR, false);
00564 DEBUG(misc, 1, "Scan complete, found %d files", num);
00565 return num;
00566 }
00567
00568 bool TarScanner::AddFile(const char *filename, size_t basepath_length)
00569 {
00570
00571 typedef struct TarHeader {
00572 char name[100];
00573 char mode[8];
00574 char uid[8];
00575 char gid[8];
00576 char size[12];
00577 char mtime[12];
00578 char chksum[8];
00579 char typeflag;
00580 char linkname[100];
00581 char magic[6];
00582 char version[2];
00583 char uname[32];
00584 char gname[32];
00585 char devmajor[8];
00586 char devminor[8];
00587 char prefix[155];
00588
00589 char unused[12];
00590 } TarHeader;
00591
00592
00593 TarList::iterator it = _tar_list.find(filename);
00594 if (it != _tar_list.end()) return false;
00595
00596 FILE *f = fopen(filename, "rb");
00597
00598
00599
00600
00601 if (f == NULL) return false;
00602
00603 const char *dupped_filename = strdup(filename);
00604 _tar_list[filename].filename = dupped_filename;
00605 _tar_list[filename].dirname = NULL;
00606
00607 TarLinkList links;
00608
00609 TarHeader th;
00610 char buf[sizeof(th.name) + 1], *end;
00611 char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
00612 char link[sizeof(th.linkname) + 1];
00613 char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
00614 size_t num = 0, pos = 0;
00615
00616
00617 char empty[512];
00618 memset(&empty[0], 0, sizeof(empty));
00619
00620 for (;;) {
00621 size_t num_bytes_read = fread(&th, 1, 512, f);
00622 if (num_bytes_read != 512) break;
00623 pos += num_bytes_read;
00624
00625
00626 if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
00627
00628 if (memcmp(&th, &empty[0], 512) == 0) continue;
00629
00630 DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
00631 return false;
00632 }
00633
00634 name[0] = '\0';
00635 size_t len = 0;
00636
00637
00638 if (th.prefix[0] != '\0') {
00639 memcpy(name, th.prefix, sizeof(th.prefix));
00640 name[sizeof(th.prefix)] = '\0';
00641 len = strlen(name);
00642 name[len] = PATHSEPCHAR;
00643 len++;
00644 }
00645
00646
00647 memcpy(&name[len], th.name, sizeof(th.name));
00648 name[len + sizeof(th.name)] = '\0';
00649
00650
00651 memcpy(buf, th.size, sizeof(th.size));
00652 buf[sizeof(th.size)] = '\0';
00653 size_t skip = strtoul(buf, &end, 8);
00654
00655 switch (th.typeflag) {
00656 case '\0':
00657 case '0': {
00658
00659 if (skip == 0) break;
00660
00661 if (strlen(name) == 0) break;
00662
00663
00664 TarFileListEntry entry;
00665 entry.tar_filename = dupped_filename;
00666 entry.size = skip;
00667 entry.position = pos;
00668
00669
00670 SimplifyFileName(name);
00671
00672 DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
00673 if (_tar_filelist.insert(TarFileList::value_type(name, entry)).second) num++;
00674
00675 break;
00676 }
00677
00678 case '1':
00679 case '2': {
00680
00681 memcpy(link, th.linkname, sizeof(th.linkname));
00682 link[sizeof(th.linkname)] = '\0';
00683
00684 if (strlen(name) == 0 || strlen(link) == 0) break;
00685
00686
00687 SimplifyFileName(name);
00688 SimplifyFileName(link);
00689
00690
00691 if (link[0] == PATHSEPCHAR) {
00692 DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
00693 break;
00694 }
00695
00696
00697
00698 strecpy(dest, name, lastof(dest));
00699 char *destpos = strrchr(dest, PATHSEPCHAR);
00700 if (destpos == NULL) destpos = dest;
00701 *destpos = '\0';
00702
00703 char *pos = link;
00704 while (*pos != '\0') {
00705 char *next = strchr(link, PATHSEPCHAR);
00706 if (next == NULL) next = pos + strlen(pos);
00707
00708
00709 if (next != pos + 1 || pos[0] != '.') {
00710 if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') {
00711
00712 if (dest[0] == '\0') {
00713 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
00714 break;
00715 }
00716
00717
00718
00719 destpos = strrchr(dest, PATHSEPCHAR);
00720 if (destpos == NULL) destpos = dest;
00721 } else {
00722
00723 if (destpos != dest) *(destpos++) = PATHSEPCHAR;
00724 strncpy(destpos, pos, next - pos);
00725 destpos += next - pos;
00726 }
00727 *destpos = '\0';
00728 }
00729
00730 pos = next;
00731 }
00732
00733
00734 DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
00735 links.insert(TarLinkList::value_type(name, dest));
00736
00737 break;
00738 }
00739
00740 case '5':
00741
00742 SimplifyFileName(name);
00743
00744
00745 DEBUG(misc, 6, "Found dir in tar: %s", name);
00746 if (_tar_list[filename].dirname == NULL) _tar_list[filename].dirname = strdup(name);
00747 break;
00748
00749 default:
00750
00751 break;
00752 }
00753
00754
00755 skip = Align(skip, 512);
00756 fseek(f, skip, SEEK_CUR);
00757 pos += skip;
00758 }
00759
00760 DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
00761 fclose(f);
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
00773 const std::string &src = link->first;
00774 const std::string &dest = link->second;
00775 TarAddLink(src, dest);
00776 }
00777
00778 return true;
00779 }
00780
00787 bool ExtractTar(const char *tar_filename)
00788 {
00789 TarList::iterator it = _tar_list.find(tar_filename);
00790
00791 if (it == _tar_list.end()) return false;
00792
00793 const char *dirname = (*it).second.dirname;
00794
00795
00796 if (dirname == NULL) return false;
00797
00798 char filename[MAX_PATH];
00799 strecpy(filename, tar_filename, lastof(filename));
00800 char *p = strrchr(filename, PATHSEPCHAR);
00801
00802 if (p == NULL) return false;
00803
00804 p++;
00805 strecpy(p, dirname, lastof(filename));
00806 DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
00807 FioCreateDirectory(filename);
00808
00809 for (TarFileList::iterator it2 = _tar_filelist.begin(); it2 != _tar_filelist.end(); it2++) {
00810 if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
00811
00812 strecpy(p, (*it2).first.c_str(), lastof(filename));
00813
00814 DEBUG(misc, 9, " extracting %s", filename);
00815
00816
00817 size_t to_copy = 0;
00818 FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
00819 if (in == NULL) {
00820 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
00821 return false;
00822 }
00823
00824
00825 FILE *out = fopen(filename, "wb");
00826 if (out == NULL) {
00827 DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
00828 fclose(in);
00829 return false;
00830 }
00831
00832
00833 char buffer[4096];
00834 size_t read;
00835 for (; to_copy != 0; to_copy -= read) {
00836 read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
00837 if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
00838 }
00839
00840
00841 fclose(in);
00842 fclose(out);
00843
00844 if (to_copy != 0) {
00845 DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
00846 return false;
00847 }
00848 }
00849
00850 DEBUG(misc, 9, " extraction successful");
00851 return true;
00852 }
00853
00854 #if defined(WIN32) || defined(WINCE)
00855
00860 extern void DetermineBasePaths(const char *exe);
00861 #else
00862
00870 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
00871 {
00872 bool success = false;
00873 #ifdef WITH_COCOA
00874 char *app_bundle = strchr(exe, '.');
00875 while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
00876
00877 if (app_bundle != NULL) app_bundle[0] = '\0';
00878 #endif
00879 char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
00880 if (s != NULL) {
00881 *s = '\0';
00882 #if defined(__DJGPP__)
00883
00884 if (s[-1] == ':') chdir("/");
00885 #endif
00886 if (chdir(exe) != 0) {
00887 DEBUG(misc, 0, "Directory with the binary does not exist?");
00888 } else {
00889 success = true;
00890 }
00891 *s = PATHSEPCHAR;
00892 }
00893 #ifdef WITH_COCOA
00894 if (app_bundle != NULL) app_bundle[0] = '.';
00895 #endif
00896 return success;
00897 }
00898
00909 bool DoScanWorkingDirectory()
00910 {
00911
00912 if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
00913
00914
00915 if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
00916
00917
00918 if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
00919
00920 char tmp[MAX_PATH];
00921 snprintf(tmp, lengthof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
00922 AppendPathSeparator(tmp, MAX_PATH);
00923 return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
00924 }
00925
00930 void DetermineBasePaths(const char *exe)
00931 {
00932 char tmp[MAX_PATH];
00933 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
00934 _searchpaths[SP_PERSONAL_DIR] = NULL;
00935 #else
00936 #ifdef __HAIKU__
00937 BPath path;
00938 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
00939 const char *homedir = path.Path();
00940 #else
00941 const char *homedir = getenv("HOME");
00942
00943 if (homedir == NULL) {
00944 const struct passwd *pw = getpwuid(getuid());
00945 homedir = (pw == NULL) ? "" : pw->pw_dir;
00946 }
00947 #endif
00948
00949 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
00950 AppendPathSeparator(tmp, MAX_PATH);
00951
00952 _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
00953 #endif
00954
00955 #if defined(WITH_SHARED_DIR)
00956 snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
00957 AppendPathSeparator(tmp, MAX_PATH);
00958 _searchpaths[SP_SHARED_DIR] = strdup(tmp);
00959 #else
00960 _searchpaths[SP_SHARED_DIR] = NULL;
00961 #endif
00962
00963 #if defined(__MORPHOS__) || defined(__AMIGA__)
00964 _searchpaths[SP_WORKING_DIR] = NULL;
00965 #else
00966 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
00967 AppendPathSeparator(tmp, MAX_PATH);
00968 _searchpaths[SP_WORKING_DIR] = strdup(tmp);
00969 #endif
00970
00971 _do_scan_working_directory = DoScanWorkingDirectory();
00972
00973
00974 if (ChangeWorkingDirectoryToExecutable(exe)) {
00975 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
00976 AppendPathSeparator(tmp, MAX_PATH);
00977 _searchpaths[SP_BINARY_DIR] = strdup(tmp);
00978 } else {
00979 _searchpaths[SP_BINARY_DIR] = NULL;
00980 }
00981
00982 if (_searchpaths[SP_WORKING_DIR] != NULL) {
00983
00984 if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
00985 DEBUG(misc, 0, "Failed to return to working directory!");
00986 }
00987 }
00988
00989 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
00990 _searchpaths[SP_INSTALLATION_DIR] = NULL;
00991 #else
00992 snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
00993 AppendPathSeparator(tmp, MAX_PATH);
00994 _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
00995 #endif
00996 #ifdef WITH_COCOA
00997 extern void cocoaSetApplicationBundleDir();
00998 cocoaSetApplicationBundleDir();
00999 #else
01000 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
01001 #endif
01002 }
01003 #endif
01004
01005 char *_personal_dir;
01006
01013 void DeterminePaths(const char *exe)
01014 {
01015 DetermineBasePaths(exe);
01016
01017 Searchpath sp;
01018 FOR_ALL_SEARCHPATHS(sp) {
01019 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01020 DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
01021 }
01022
01023 if (_config_file != NULL) {
01024 _personal_dir = strdup(_config_file);
01025 char *end = strrchr(_personal_dir, PATHSEPCHAR);
01026 if (end == NULL) {
01027 _personal_dir[0] = '\0';
01028 } else {
01029 end[1] = '\0';
01030 }
01031 } else {
01032 char personal_dir[MAX_PATH];
01033 FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg");
01034
01035 if (FileExists(personal_dir)) {
01036 char *end = strrchr(personal_dir, PATHSEPCHAR);
01037 if (end != NULL) end[1] = '\0';
01038 _personal_dir = strdup(personal_dir);
01039 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01040 } else {
01041 static const Searchpath new_openttd_cfg_order[] = {
01042 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
01043 };
01044
01045 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
01046 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
01047 _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
01048 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01049 break;
01050 }
01051 }
01052 }
01053 }
01054
01055 DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
01056
01057 _highscore_file = str_fmt("%shs.dat", _personal_dir);
01058 extern char *_hotkeys_file;
01059 _hotkeys_file = str_fmt("%shotkeys.cfg", _personal_dir);
01060
01061
01062 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
01063 FioCreateDirectory(_personal_dir);
01064 #endif
01065
01066 static const Subdirectory default_subdirs[] = {
01067 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR
01068 };
01069
01070 for (uint i = 0; i < lengthof(default_subdirs); i++) {
01071 char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
01072 FioCreateDirectory(dir);
01073 free(dir);
01074 }
01075
01076
01077 _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
01078 #ifdef ENABLE_NETWORK
01079 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01080
01081
01082 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, DATA_DIR, AI_DIR, AI_LIBRARY_DIR, GM_DIR };
01083 for (uint i = 0; i < lengthof(dirs); i++) {
01084 char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
01085 FioCreateDirectory(tmp);
01086 free(tmp);
01087 }
01088
01089 extern char *_log_file;
01090 _log_file = str_fmt("%sopenttd.log", _personal_dir);
01091 #else
01092
01093
01094 if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
01095 free((void*)_searchpaths[SP_AUTODOWNLOAD_DIR]);
01096 _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
01097 }
01098 #endif
01099
01100 TarScanner::DoScan();
01101 }
01102
01107 void SanitizeFilename(char *filename)
01108 {
01109 for (; *filename != '\0'; filename++) {
01110 switch (*filename) {
01111
01112
01113 case ':': case '\\': case '*': case '?': case '/':
01114 case '<': case '>': case '|': case '"':
01115 *filename = '_';
01116 break;
01117 }
01118 }
01119 }
01120
01121 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
01122 {
01123 FILE *in = fopen(filename, "rb");
01124 if (in == NULL) return NULL;
01125
01126 fseek(in, 0, SEEK_END);
01127 size_t len = ftell(in);
01128 fseek(in, 0, SEEK_SET);
01129 if (len > maxsize) {
01130 fclose(in);
01131 return NULL;
01132 }
01133 byte *mem = MallocT<byte>(len + 1);
01134 mem[len] = 0;
01135 if (fread(mem, len, 1, in) != 1) {
01136 fclose(in);
01137 free(mem);
01138 return NULL;
01139 }
01140 fclose(in);
01141
01142 *lenp = len;
01143 return mem;
01144 }
01145
01146
01156 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
01157 {
01158 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
01159
01160 uint num = 0;
01161 struct stat sb;
01162 struct dirent *dirent;
01163 DIR *dir;
01164
01165 if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
01166
01167 while ((dirent = readdir(dir)) != NULL) {
01168 const char *d_name = FS2OTTD(dirent->d_name);
01169 char filename[MAX_PATH];
01170
01171 if (!FiosIsValidFile(path, dirent, &sb)) continue;
01172
01173 snprintf(filename, lengthof(filename), "%s%s", path, d_name);
01174
01175 if (S_ISDIR(sb.st_mode)) {
01176
01177 if (!recursive) continue;
01178 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01179 if (!AppendPathSeparator(filename, lengthof(filename))) continue;
01180 num += ScanPath(fs, extension, filename, basepath_length, recursive);
01181 } else if (S_ISREG(sb.st_mode)) {
01182
01183 if (extension != NULL) {
01184 char *ext = strrchr(filename, '.');
01185
01186
01187 if (ext == NULL) continue;
01188 if (strcasecmp(ext, extension) != 0) continue;
01189 }
01190
01191 if (fs->AddFile(filename, basepath_length)) num++;
01192 }
01193 }
01194
01195 closedir(dir);
01196
01197 return num;
01198 }
01199
01206 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01207 {
01208 uint num = 0;
01209 const char *filename = (*tar).first.c_str();
01210
01211 if (extension != NULL) {
01212 const char *ext = strrchr(filename, '.');
01213
01214
01215 if (ext == NULL) return false;
01216 if (strcasecmp(ext, extension) != 0) return false;
01217 }
01218
01219 if (fs->AddFile(filename, 0)) num++;
01220
01221 return num;
01222 }
01223
01233 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
01234 {
01235 Searchpath sp;
01236 char path[MAX_PATH];
01237 TarFileList::iterator tar;
01238 uint num = 0;
01239
01240 FOR_ALL_SEARCHPATHS(sp) {
01241
01242 if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01243
01244 FioAppendDirectory(path, MAX_PATH, sp, sd);
01245 num += ScanPath(this, extension, path, strlen(path), recursive);
01246 }
01247
01248 if (tars) {
01249 FOR_ALL_TARS(tar) {
01250 num += ScanTar(this, extension, tar);
01251 }
01252 }
01253
01254 return num;
01255 }
01256
01265 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
01266 {
01267 char path[MAX_PATH];
01268 strecpy(path, directory, lastof(path));
01269 if (!AppendPathSeparator(path, lengthof(path))) return 0;
01270 return ScanPath(this, extension, path, strlen(path), recursive);
01271 }