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