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