18 # define access _taccess
19 #elif defined(__HAIKU__)
21 #include <storage/FindDirectory.h>
33 #define FIO_BUFFER_SIZE 512
42 std::array<FILE *, MAX_FILE_SLOTS>
handles;
115 if (size == 0)
return 0;
117 return *
_fio.buffer++;
201 if (f ==
nullptr)
usererror(
"Cannot open file '%s'", filename.c_str());
203 if (pos < 0)
usererror(
"Cannot read file '%s'", filename.c_str());
210 auto t = filename.rfind(PATHSEPCHAR);
211 std::string sn = filename.substr(t != std::string::npos ? t + 1 : 0);
218 static const char *
const _subdirs[] = {
221 "save" PATHSEP
"autosave" PATHSEP,
223 "scenario" PATHSEP
"heightmap" PATHSEP,
230 "ai" PATHSEP
"library" PATHSEP,
232 "game" PATHSEP
"library" PATHSEP,
233 "screenshot" PATHSEP,
244 std::array<TarList, NUM_SUBDIRS> _tar_list;
247 typedef std::map<std::string, std::string> TarLinkList;
269 if (f ==
nullptr)
return false;
282 return access(
OTTD2FS(filename.c_str()), 0) == 0;
305 std::string buf = FioGetDirectory(sp, subdir);
322 assert(sp < NUM_SEARCHPATHS);
333 std::string ret = FioGetDirectory(sp, subdir);
341 static FILE *FioFOpenFileSp(
const std::string &filename,
const char *mode,
Searchpath sp,
Subdirectory subdir,
size_t *filesize)
349 MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode,
lengthof(Lmode));
361 if (mode[0] ==
'r' && GetFileAttributes(
OTTD2FS(buf.c_str())) == INVALID_FILE_ATTRIBUTES)
return nullptr;
364 f = fopen(buf.c_str(), mode);
367 f = fopen(buf.c_str(), mode);
370 if (f !=
nullptr && filesize !=
nullptr) {
372 fseek(f, 0, SEEK_END);
373 *filesize = ftell(f);
374 fseek(f, 0, SEEK_SET);
388 FILE *f = fopen(entry.tar_filename.c_str(),
"rb");
389 if (f ==
nullptr)
return f;
391 if (fseek(f, entry.position, SEEK_SET) < 0) {
396 if (filesize !=
nullptr) *filesize = entry.size;
414 f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
419 if (f ==
nullptr && mode[0] ==
'r' && subdir !=
NO_DIRECTORY) {
421 std::string resolved_name = filename;
425 std::istringstream ss(resolved_name);
426 std::vector<std::string> tokens;
428 while (std::getline(ss, token, PATHSEPCHAR)) {
430 if (tokens.size() < 2)
return nullptr;
432 }
else if (token ==
".") {
436 tokens.push_back(token);
440 resolved_name.clear();
442 for (
const std::string &token : tokens) {
444 resolved_name += PATHSEP;
446 resolved_name += token;
452 const std::string &src = link->first;
453 size_t len = src.length();
454 if (resolved_name.length() >= len && resolved_name[len - 1] == PATHSEPCHAR && src.compare(0, len, resolved_name, 0, len) == 0) {
456 resolved_name.replace(0, len, link->second);
461 TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
462 if (it != _tar_filelist[subdir].end()) {
473 if (f !=
nullptr)
break;
495 auto p = name.find_last_of(PATHSEPCHAR);
496 if (p != std::string::npos) {
497 std::string dirname = name.substr(0, p);
499 if (dir ==
nullptr) {
509 CreateDirectory(
OTTD2FS(name.c_str()),
nullptr);
510 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
513 mkdir(
OTTD2FS(name.c_str()), 0755);
525 if (buf.empty())
return;
527 if (buf.back() != PATHSEPCHAR) buf.push_back(PATHSEPCHAR);
530 static void TarAddLink(
const std::string &srcParam,
const std::string &destParam,
Subdirectory subdir)
532 std::string src = srcParam;
533 std::string dest = destParam;
535 std::transform(src.begin(), src.end(), src.begin(), tolower);
536 std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
538 TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
539 if (dest_file != _tar_filelist[subdir].end()) {
541 _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
545 const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
546 const std::string dst_path = (dest.length() == 0 ?
"" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
547 _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
562 #if (PATHSEPCHAR != '/')
563 for (
char *n = name; *n !=
'\0'; n++)
if (*n ==
'/') *n = PATHSEPCHAR;
574 _tar_filelist[sd].clear();
575 _tar_list[sd].clear();
576 uint num = this->
Scan(
".tar", sd,
false);
583 DEBUG(misc, 1,
"Scanning for tars");
604 DEBUG(misc, 1,
"Scan complete, found %d files", num);
617 return this->
AddFile(filename, 0);
620 bool TarScanner::AddFile(
const std::string &filename,
size_t basepath_length,
const std::string &tar_filename)
623 assert(tar_filename.empty());
648 TarList::iterator it = _tar_list[this->
subdir].find(filename);
649 if (it != _tar_list[this->subdir].end())
return false;
651 FILE *f = fopen(filename.c_str(),
"rb");
656 if (f ==
nullptr)
return false;
658 _tar_list[this->
subdir][filename] = std::string{};
663 char buf[
sizeof(th.name) + 1], *end;
664 char name[
sizeof(th.prefix) + 1 +
sizeof(th.name) + 1];
665 char link[
sizeof(th.linkname) + 1];
666 char dest[
sizeof(th.prefix) + 1 +
sizeof(th.name) + 1 + 1 +
sizeof(th.linkname) + 1];
667 size_t num = 0, pos = 0;
671 memset(&empty[0], 0,
sizeof(empty));
674 size_t num_bytes_read = fread(&th, 1, 512, f);
675 if (num_bytes_read != 512)
break;
676 pos += num_bytes_read;
679 if (strncmp(th.magic,
"ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
681 if (memcmp(&th, &empty[0], 512) == 0)
continue;
683 DEBUG(misc, 0,
"The file '%s' isn't a valid tar-file", filename.c_str());
691 if (th.prefix[0] !=
'\0') {
701 size_t skip = strtoul(buf, &end, 8);
703 switch (th.typeflag) {
707 if (skip == 0)
break;
709 if (strlen(name) == 0)
break;
713 entry.tar_filename = filename;
715 entry.position = pos;
720 DEBUG(misc, 6,
"Found file in tar: %s (" PRINTF_SIZE
" bytes, " PRINTF_SIZE
" offset)", name, skip, pos);
721 if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
731 if (strlen(name) == 0 || strlen(link) == 0)
break;
738 if (link[0] == PATHSEPCHAR) {
739 DEBUG(misc, 1,
"Ignoring absolute link in tar: %s -> %s", name, link);
746 char *destpos = strrchr(dest, PATHSEPCHAR);
747 if (destpos ==
nullptr) destpos = dest;
751 while (*pos !=
'\0') {
752 char *next = strchr(pos, PATHSEPCHAR);
753 if (next ==
nullptr) {
754 next = pos + strlen(pos);
760 if (strcmp(pos,
".") == 0) {
762 }
else if (strcmp(pos,
"..") == 0) {
764 if (dest[0] ==
'\0') {
765 DEBUG(misc, 1,
"Ignoring link pointing outside of data directory: %s -> %s", name, link);
771 destpos = strrchr(dest, PATHSEPCHAR);
772 if (destpos ==
nullptr) destpos = dest;
776 if (destpos != dest) destpos =
strecpy(destpos, PATHSEP,
lastof(dest));
780 if (destpos >=
lastof(dest)) {
781 DEBUG(misc, 0,
"The length of a link in tar-file '%s' is too large (malformed?)", filename.c_str());
790 DEBUG(misc, 6,
"Found link in tar: %s -> %s", name, dest);
791 links.insert(TarLinkList::value_type(name, dest));
801 DEBUG(misc, 6,
"Found dir in tar: %s", name);
802 if (_tar_list[this->subdir][filename].empty()) _tar_list[this->
subdir][filename] = name;
811 skip =
Align(skip, 512);
812 if (fseek(f, skip, SEEK_CUR) < 0) {
813 DEBUG(misc, 0,
"The file '%s' can't be read as a valid tar-file", filename.c_str());
820 DEBUG(misc, 1,
"Found tar '%s' with " PRINTF_SIZE
" new files", filename.c_str(), num);
832 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
833 const std::string &src = link->first;
834 const std::string &dest = link->second;
835 TarAddLink(src, dest, this->subdir);
850 TarList::iterator it = _tar_list[subdir].find(tar_filename);
852 if (it == _tar_list[subdir].end())
return false;
854 const auto &dirname = (*it).second;
857 if (dirname.empty()) {
858 DEBUG(misc, 1,
"Extracting %s failed; archive rejected, the contents must be in a sub directory", tar_filename.c_str());
862 std::string filename = tar_filename;
863 auto p = filename.find_last_of(PATHSEPCHAR);
865 if (p == std::string::npos)
return false;
867 filename.replace(p + 1, std::string::npos, dirname);
868 DEBUG(misc, 8,
"Extracting %s to directory %s", tar_filename.c_str(), filename.c_str());
871 for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
872 if (tar_filename != it2->second.tar_filename)
continue;
874 filename.replace(p + 1, std::string::npos, it2->first);
876 DEBUG(misc, 9,
" extracting %s", filename.c_str());
880 std::unique_ptr<FILE, FileDeleter> in(
FioFOpenFileTar(it2->second, &to_copy));
882 DEBUG(misc, 6,
"Extracting %s failed; could not open %s", filename.c_str(), tar_filename.c_str());
887 std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(),
"wb"));
889 DEBUG(misc, 6,
"Extracting %s failed; could not open %s", filename.c_str(), filename.c_str());
896 for (; to_copy != 0; to_copy -= read) {
897 read = fread(buffer, 1, std::min(to_copy,
lengthof(buffer)), in.get());
898 if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read)
break;
902 DEBUG(misc, 6,
"Extracting %s failed; still %i bytes to copy", filename.c_str(), (
int)to_copy);
907 DEBUG(misc, 9,
" extraction successful");
932 bool success =
false;
934 char *app_bundle = strchr(tmp,
'.');
935 while (app_bundle !=
nullptr && strncasecmp(app_bundle,
".app", 4) != 0) app_bundle = strchr(&app_bundle[1],
'.');
937 if (app_bundle !=
nullptr) *app_bundle =
'\0';
939 char *s = strrchr(tmp, PATHSEPCHAR);
942 if (chdir(tmp) != 0) {
943 DEBUG(misc, 0,
"Directory with the binary does not exist?");
987 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
988 return std::string(path.Path());
990 const char *home_env = getenv(
"HOME");
991 if (home_env !=
nullptr)
return std::string(home_env);
993 const struct passwd *pw = getpwuid(getuid());
994 if (pw !=
nullptr)
return std::string(pw->pw_dir);
1008 const char *xdg_data_home = getenv(
"XDG_DATA_HOME");
1009 if (xdg_data_home !=
nullptr) {
1010 tmp = xdg_data_home;
1012 tmp += PERSONAL_DIR[0] ==
'.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1016 tmp +=
"content_download";
1019 }
else if (!homedir.empty()) {
1021 tmp += PATHSEP
".local" PATHSEP
"share" PATHSEP;
1022 tmp += PERSONAL_DIR[0] ==
'.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1026 tmp +=
"content_download";
1035 #if defined(OS2) || !defined(WITH_PERSONAL_DIR)
1038 if (!homedir.empty()) {
1041 tmp += PERSONAL_DIR;
1045 tmp +=
"content_download";
1054 #if defined(WITH_SHARED_DIR)
1063 if (getcwd(cwd, MAX_PATH) ==
nullptr) *cwd =
'\0';
1075 if (end == std::string::npos) {
1088 if (getcwd(buf,
lengthof(buf)) ==
nullptr) {
1099 if (cwd[0] !=
'\0') {
1101 if (chdir(cwd) != 0) {
1102 DEBUG(misc, 0,
"Failed to return to working directory!");
1106 #if !defined(GLOBAL_DATA_DIR)
1109 tmp = GLOBAL_DATA_DIR;
1114 extern void CocoaSetApplicationBundleDir();
1115 CocoaSetApplicationBundleDir();
1135 std::string config_home;
1137 const char *xdg_config_home = getenv(
"XDG_CONFIG_HOME");
1138 if (xdg_config_home !=
nullptr) {
1139 config_home = xdg_config_home;
1140 config_home += PATHSEP;
1141 config_home += PERSONAL_DIR[0] ==
'.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1142 }
else if (!homedir.empty()) {
1144 config_home = homedir;
1145 config_home += PATHSEP
".config" PATHSEP;
1146 config_home += PERSONAL_DIR[0] ==
'.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1157 std::string config_dir;
1162 if (!personal_dir.empty()) {
1163 auto end = personal_dir.find_last_of(PATHSEPCHAR);
1164 if (end != std::string::npos) personal_dir.erase(end + 1);
1165 config_dir = personal_dir;
1169 config_dir = config_home;
1171 static const Searchpath new_openttd_cfg_order[] = {
1176 for (uint i = 0; i <
lengthof(new_openttd_cfg_order); i++) {
1187 DEBUG(misc, 3,
"%s found as config directory", config_dir.c_str());
1190 extern std::string _hotkeys_file;
1191 _hotkeys_file = config_dir +
"hotkeys.cfg";
1196 if (config_dir == config_home) {
1208 #if defined(WITH_PERSONAL_DIR)
1215 SAVE_DIR,
AUTOSAVE_DIR,
SCENARIO_DIR,
HEIGHTMAP_DIR,
BASESET_DIR,
NEWGRF_DIR,
AI_DIR,
AI_LIBRARY_DIR,
GAME_DIR,
GAME_LIBRARY_DIR,
SCREENSHOT_DIR
1218 for (uint i = 0; i <
lengthof(default_subdirs); i++) {
1228 for (uint i = 0; i <
lengthof(dirs); i++) {
1242 for (; *filename !=
'\0'; filename++) {
1243 switch (*filename) {
1246 case ':':
case '\\':
case '*':
case '?':
case '/':
1247 case '<':
case '>':
case '|':
case '"':
1262 std::unique_ptr<char>
ReadFileToMem(
const std::string &filename,
size_t &lenp,
size_t maxsize)
1264 FILE *in = fopen(filename.c_str(),
"rb");
1265 if (in ==
nullptr)
return nullptr;
1269 fseek(in, 0, SEEK_END);
1270 size_t len = ftell(in);
1271 fseek(in, 0, SEEK_SET);
1272 if (len > maxsize)
return nullptr;
1277 std::unique_ptr<char> mem(
static_cast<char *
>(::
operator new(len + 1)));
1280 if (fread(mem.get(), len, 1, in) != 1)
return nullptr;
1294 if (extension ==
nullptr)
return true;
1296 const char *ext = strrchr(filename, extension[0]);
1297 return ext !=
nullptr && strcasecmp(ext, extension) == 0;
1309 static uint
ScanPath(
FileScanner *fs,
const char *extension,
const char *path,
size_t basepath_length,
bool recursive)
1311 extern bool FiosIsValidFile(
const char *path,
const struct dirent *ent,
struct stat *sb);
1315 struct dirent *dirent;
1318 if (path ==
nullptr || (dir =
ttd_opendir(path)) ==
nullptr)
return 0;
1320 while ((dirent = readdir(dir)) !=
nullptr) {
1321 const char *d_name =
FS2OTTD(dirent->d_name);
1323 if (!FiosIsValidFile(path, dirent, &sb))
continue;
1325 std::string filename(path);
1328 if (S_ISDIR(sb.st_mode)) {
1330 if (!recursive)
continue;
1331 if (strcmp(d_name,
".") == 0 || strcmp(d_name,
"..") == 0)
continue;
1333 num +=
ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
1334 }
else if (S_ISREG(sb.st_mode)) {
1354 const auto &filename = (*tar).first;
1356 if (
MatchesExtension(extension, filename.c_str()) && fs->
AddFile(filename, 0, (*tar).second.tar_filename)) num++;
1375 TarFileList::iterator tar;
1382 std::string path = FioGetDirectory(sp, sd);
1383 num +=
ScanPath(
this, extension, path.c_str(), path.size(), recursive);
1387 FOR_ALL_TARS(tar, sd) {
1388 num +=
ScanTar(
this, extension, tar);
1416 std::string path(directory);
1418 return ScanPath(
this, extension, path.c_str(), path.size(), recursive);