19 #define GETTEXT_DOMAIN "wesnoth-lib" 33 #include <boost/algorithm/string.hpp> 34 #include <boost/filesystem.hpp> 35 #include <boost/filesystem/fstream.hpp> 36 #include <boost/iostreams/device/file_descriptor.hpp> 37 #include <boost/iostreams/stream.hpp> 43 #include <boost/locale.hpp> 50 #ifndef VOLUME_NAME_NONE 51 #define VOLUME_NAME_NONE 0x4 60 #if defined(__APPLE__) && defined(__MACH__) && defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) 62 #define WESNOTH_BOOST_OS_IOS (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__*1000) 63 #include <SDL2/SDL_filesystem.h> 69 #define DBG_FS LOG_STREAM(debug, log_filesystem) 70 #define LOG_FS LOG_STREAM(info, log_filesystem) 71 #define WRN_FS LOG_STREAM(warn, log_filesystem) 72 #define ERR_FS LOG_STREAM(err, log_filesystem) 74 namespace bfs = boost::filesystem;
75 using boost::system::error_code;
80 const std::string maincfg_filename =
"_main.cfg";
81 const std::string finalcfg_filename =
"_final.cfg";
82 const std::string initialcfg_filename =
"_initial.cfg";
85 class customcodecvt :
public std::codecvt<wchar_t , char , std::mbstate_t>
89 template<
typename char_t_to>
90 struct customcodecvt_do_conversion_writer
92 customcodecvt_do_conversion_writer(char_t_to*& _to_next, char_t_to* _to_end)
101 bool can_push(std::size_t count)
const 103 return static_cast<std::size_t
>(to_end - to_next) > count;
106 void push(char_t_to val)
108 assert(to_next != to_end);
113 template<
typename char_t_from,
typename char_t_to>
114 static void customcodecvt_do_conversion(std::mbstate_t& ,
115 const char_t_from* from,
116 const char_t_from* from_end,
117 const char_t_from*& from_next,
127 customcodecvt_do_conversion_writer<char_t_to>
writer(to_next, to_end);
129 while(from_next != from_end) {
136 int do_encoding()
const noexcept
142 bool do_always_noconv()
const noexcept
147 int do_length(std::mbstate_t& ,
const char* ,
const char* , std::size_t )
const 150 throw "Not supported";
153 std::codecvt_base::result unshift(
154 std::mbstate_t& ,
char* ,
char* ,
char*& )
const 157 throw "Not supported";
161 std::codecvt_base::result do_in(std::mbstate_t& state,
163 const char* from_end,
164 const char*& from_next,
167 wchar_t*& to_next)
const 170 customcodecvt_do_conversion<char, wchar_t>(state, from, from_end, from_next, to, to_end, to_next);
172 ERR_FS <<
"Invalid UTF-8 string'" << std::string(from, from_end) <<
"' " << std::endl;
176 return std::codecvt_base::ok;
179 std::codecvt_base::result do_out(std::mbstate_t& state,
181 const wchar_t* from_end,
182 const wchar_t*& from_next,
185 char*& to_next)
const 188 customcodecvt_do_conversion<wchar_t, char>(state, from, from_end, from_next, to, to_end, to_next);
190 ERR_FS <<
"Invalid UTF-16 string" << std::endl;
194 return std::codecvt_base::ok;
208 utf8_loc = std::locale(utf8_loc,
new customcodecvt());
210 boost::filesystem::path::imbue(utf8_loc);
214 static static_runner static_bfs_path_imbuer;
216 bool is_filename_case_correct(
const std::string& fname,
const boost::iostreams::file_descriptor_source& fd)
218 wchar_t real_path[MAX_PATH];
219 GetFinalPathNameByHandleW(fd.handle(), real_path, MAX_PATH - 1, VOLUME_NAME_NONE);
226 bool is_filename_case_correct(
const std::string& ,
const boost::iostreams::file_descriptor_source& )
240 vec->push_back(file.generic_string());
242 vec->push_back(file.filename().generic_string());
249 return ec && ec != boost::system::errc::no_such_file_or_directory;
257 LOG_FS <<
"Failed to check if " << fpath.string() <<
" is a directory: " << ec.message() <<
'\n';
268 ERR_FS <<
"Failed to check existence of file " << fpath.string() <<
": " << ec.message() <<
'\n';
279 bfs::create_directory(dirpath, ec);
282 ERR_FS <<
"Failed to create directory " << dirpath.string() <<
": " << ec.message() <<
'\n';
290 ERR_FS <<
"Could not open or create directory " << dirpath.string() <<
'\n';
291 return std::string();
300 bfs::file_status
fs = bfs::status(dirpath, ec);
303 ERR_FS <<
"Failed to retrieve file status for " << dirpath.string() <<
": " << ec.message() <<
'\n';
306 DBG_FS <<
"directory " << dirpath.string() <<
" exists, not creating\n";
309 ERR_FS <<
"cannot create directory " << dirpath.string() <<
"; file exists\n";
313 bool created = bfs::create_directory(dirpath, ec);
315 ERR_FS <<
"Failed to create directory " << dirpath.string() <<
": " << ec.message() <<
'\n';
323 DBG_FS <<
"creating recursive directory: " << dirpath.string() <<
'\n';
325 if(dirpath.empty()) {
330 bfs::file_status
fs = bfs::status(dirpath);
333 ERR_FS <<
"Failed to retrieve file status for " << dirpath.string() <<
": " << ec.message() <<
'\n';
344 ERR_FS <<
"Could not create parents to " << dirpath.string() <<
'\n';
350 std::vector<std::string>* files,
351 std::vector<std::string>* dirs,
362 get_files_in_dir(absolute_dir.string(), files, dirs, mode, filter, reorder, checksum);
369 if(reorder == reorder_mode::DO_REORDER) {
370 LOG_FS <<
"searching for _main.cfg in directory " << dir <<
'\n';
371 const bfs::path maincfg = dirpath / maincfg_filename;
374 LOG_FS <<
"_main.cfg found : " << maincfg <<
'\n';
375 push_if_exists(files, maincfg, mode == name_mode::ENTIRE_FILE_PATH);
381 bfs::directory_iterator di(dirpath, ec);
382 bfs::directory_iterator end;
389 for(; di != end; ++di) {
390 bfs::file_status st = di->status(ec);
392 LOG_FS <<
"Failed to get file status of " << di->path().string() <<
": " << ec.message() <<
'\n';
396 if(st.type() == bfs::regular_file) {
398 std::string basename = di->path().filename().string();
399 if(filter == filter_mode::SKIP_PBL_FILES &&
looks_like_pbl(basename))
401 if(!basename.empty() && basename[0] ==
'.')
405 push_if_exists(files, di->path(), mode == name_mode::ENTIRE_FILE_PATH);
407 if(checksum !=
nullptr) {
408 std::time_t mtime = bfs::last_write_time(di->path(), ec);
410 LOG_FS <<
"Failed to read modification time of " << di->path().string() <<
": " << ec.message()
412 }
else if(mtime > checksum->
modified) {
418 LOG_FS <<
"Failed to read filesize of " << di->path().string() <<
": " << ec.message() <<
'\n';
425 }
else if(st.type() == bfs::directory_file) {
426 std::string basename = di->path().filename().string();
428 if(!basename.empty() && basename[0] ==
'.') {
432 if(filter == filter_mode::SKIP_MEDIA_DIR && (basename ==
"images" || basename ==
"sounds")) {
436 const bfs::path inner_main(di->path() / maincfg_filename);
437 bfs::file_status main_st = bfs::status(inner_main, ec);
440 LOG_FS <<
"Failed to get file status of " << inner_main.string() <<
": " << ec.message() <<
'\n';
441 }
else if(reorder == reorder_mode::DO_REORDER && main_st.type() == bfs::regular_file) {
442 LOG_FS <<
"_main.cfg found : " 443 << (mode == name_mode::ENTIRE_FILE_PATH ? inner_main.string() : inner_main.filename().string()) <<
'\n';
444 push_if_exists(files, inner_main, mode == name_mode::ENTIRE_FILE_PATH);
446 push_if_exists(dirs, di->path(), mode == name_mode::ENTIRE_FILE_PATH);
451 if(files !=
nullptr) {
455 if(dirs !=
nullptr) {
459 if(files !=
nullptr && reorder == reorder_mode::DO_REORDER) {
461 for(
unsigned int i = 0;
i < files->size();
i++) {
462 if(
ends_with((*files)[
i],
"/" + finalcfg_filename)) {
463 files->push_back((*files)[i]);
464 files->erase(files->begin() +
i);
471 for(
unsigned int i = 0;
i < files->size();
i++)
472 if(
ends_with((*files)[
i],
"/" + initialcfg_filename)) {
477 std::string initialcfg = (*files)[foundit];
478 for(
unsigned int i = foundit; i > 0; i--)
479 (*files)[
i] = (*files)[i - 1];
480 (*files)[0] = initialcfg;
492 std::string next_filename;
496 std::stringstream filename;
502 filename << counter << extension;
505 next_filename = filename.str();
506 }
while(
file_exists(next_filename) && counter < 1000);
508 return next_filename;
515 std::ostringstream
s;
522 static std::string suffix;
534 #if defined(__APPLE__) && !defined(__IPHONEOS__) 540 static void migrate_apple_config_directory_for_unsandboxed_builds()
542 const char* home_str = getenv(
"HOME");
543 bfs::path home = home_str ? home_str :
".";
548 boost::filesystem::path new_saves_dir = home /
"Library/Containers/org.wesnoth.Wesnoth/Data/Library/Application Support/Wesnoth_";
553 std::cout <<
"Apple developer's userdata migration: ";
554 std::cout <<
"symlinking " << old_saves_dir <<
" to " << new_saves_dir <<
"\n";
555 bfs::create_symlink(new_saves_dir, old_saves_dir);
556 }
else if(!bfs::symbolic_link_exists(old_saves_dir)) {
557 std::cout <<
"Apple developer's userdata migration: ";
558 std::cout <<
"Problem! Old (non-containerized) directory " << old_saves_dir <<
" is not a symlink. Your savegames are scattered around 2 locations.\n";
568 #if defined(__APPLE__) && !defined(__IPHONEOS__) 569 migrate_apple_config_directory_for_unsandboxed_builds();
573 ERR_FS <<
"could not open or create user data directory at " << user_data_dir.string() <<
'\n';
596 static bool is_path_relative_to_cwd(
const std::string& str)
604 return *p.begin() ==
"." || *p.begin() ==
"..";
610 [[maybe_unused]]
bool relative_ok =
false;
612 #ifdef PREFERENCES_DIR 613 if(newprefdir.empty()) {
614 newprefdir = PREFERENCES_DIR;
620 if(newprefdir.size() > 2 && newprefdir[1] ==
':') {
622 user_data_dir = newprefdir;
623 }
else if(is_path_relative_to_cwd(newprefdir)) {
625 user_data_dir =
get_cwd() +
"/" + newprefdir;
627 if(newprefdir.empty()) {
630 #ifdef PREFERENCES_DIR 631 if (newprefdir != PREFERENCES_DIR)
638 _(
"Use an absolute path, or a relative path that starts with a period and a backslash"));
642 PWSTR docs_path =
nullptr;
643 HRESULT res = SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_CREATE,
nullptr, &docs_path);
649 ERR_FS <<
"Could not determine path to user's Documents folder! (" << std::hex <<
"0x" << res << std::dec <<
") " 650 <<
"User config/data directories may be unavailable for " 651 <<
"this session. Please report this as a bug.\n";
657 user_data_dir = games_path / newprefdir;
660 CoTaskMemFree(docs_path);
667 #ifdef WESNOTH_BOOST_OS_IOS 668 char *sdl_pref_path = SDL_GetPrefPath(
"wesnoth.org",
"iWesnoth");
670 backupprefdir = std::string(sdl_pref_path) + backupprefdir;
671 SDL_free(sdl_pref_path);
676 const char* home_str = getenv(
"HOME");
678 if(newprefdir.empty()) {
679 char const* xdg_data = getenv(
"XDG_DATA_HOME");
680 if(!xdg_data || xdg_data[0] ==
'\0') {
682 newprefdir = backupprefdir;
686 user_data_dir = home_str;
687 user_data_dir /=
".local/share";
689 user_data_dir = xdg_data;
692 user_data_dir /=
"wesnoth";
696 bfs::path home = home_str ? home_str :
".";
698 if(newprefdir[0] ==
'/') {
699 user_data_dir = newprefdir;
706 _(
"Use absolute paths. Relative paths are deprecated because they are interpreted relative to $HOME"));
708 user_data_dir = home / newprefdir;
712 if(newprefdir.empty()) {
713 newprefdir = backupprefdir;
716 const char* home_str = getenv(
"HOME");
717 bfs::path home = home_str ? home_str :
".";
719 if(newprefdir[0] ==
'/') {
720 user_data_dir = newprefdir;
727 _(
"Use absolute paths. Relative paths are deprecated because they are interpreted relative to $HOME"));
729 user_data_dir = home / newprefdir;
735 user_data_dir =
normalize_path(user_data_dir.string(),
true,
true);
740 user_config_dir = newconfig;
742 ERR_FS <<
"could not open or create user config directory at " << user_config_dir.string() <<
'\n';
753 if(user_data_dir.empty()) {
762 if(user_config_dir.empty()) {
763 #if defined(_X11) && !defined(PREFERENCES_DIR) 764 char const* xdg_config = getenv(
"XDG_CONFIG_HOME");
766 if(!xdg_config || xdg_config[0] ==
'\0') {
767 xdg_config = getenv(
"HOME");
770 return user_config_dir.string();
773 user_config_dir = xdg_config;
774 user_config_dir /=
".config";
776 user_config_dir = xdg_config;
779 user_config_dir /=
"wesnoth";
786 return user_config_dir.string();
796 if(cache_dir.empty()) {
797 #if defined(_X11) && !defined(PREFERENCES_DIR) 798 char const* xdg_cache = getenv(
"XDG_CACHE_HOME");
800 if(!xdg_cache || xdg_cache[0] ==
'\0') {
801 xdg_cache = getenv(
"HOME");
804 return cache_dir.string();
807 cache_dir = xdg_cache;
808 cache_dir /=
".cache";
810 cache_dir = xdg_cache;
813 cache_dir /=
"wesnoth";
820 return cache_dir.string();
825 #if !defined(_WIN32) && !defined(_X11) && !defined(__APPLE__) 832 if(w_ver.major_version() != 1 || ms_ver.major_version() != 1) {
837 std::vector<other_version_dir> result;
840 for(
auto minor = ms_ver.minor_version(); minor <= w_ver.minor_version() + 4; ++minor) {
841 if(minor == w_ver.minor_version())
846 version.set_minor_version(minor);
863 #elif defined(__APPLE__) 868 result.emplace_back(suffix, path.string());
882 ERR_FS <<
"Failed to get current directory: " << ec.message() <<
'\n';
886 return cwd.generic_string();
895 ERR_FS <<
"Failed to set current directory: " << ec.message() <<
'\n';
898 LOG_FS <<
"Process working directory set to " << dir <<
'\n';
907 wchar_t process_path[MAX_PATH];
908 SetLastError(ERROR_SUCCESS);
910 GetModuleFileNameW(
nullptr, process_path, MAX_PATH);
912 if(GetLastError() != ERROR_SUCCESS) {
917 return exe.parent_path().string();
922 bfs::path exe = bfs::read_symlink(self_exe, ec);
924 return std::string();
927 return exe.parent_path().string();
937 bool created = bfs::create_directory(
bfs::path(dirname), ec);
939 ERR_FS <<
"Failed to create directory " << dirname <<
": " << ec.message() <<
'\n';
948 std::vector<std::string> files;
949 std::vector<std::string> dirs;
952 get_files_in_dir(dirname, &files, &dirs, name_mode::ENTIRE_FILE_PATH, keep_pbl ? filter_mode::SKIP_PBL_FILES : filter_mode::NO_FILTER);
955 for(
const std::string&
f : files) {
958 LOG_FS <<
"remove(" <<
f <<
"): " << ec.message() <<
'\n';
965 for(
const std::string&
d : dirs) {
976 LOG_FS <<
"remove(" << dirname <<
"): " << ec.message() <<
'\n';
989 ERR_FS <<
"Could not delete file " << filename <<
": " << ec.message() <<
'\n';
998 std::stringstream ss;
1005 LOG_FS <<
"Streaming " << fname <<
" for reading.\n";
1008 ERR_FS <<
"Trying to open file with empty name.\n";
1010 s->clear(std::ios_base::failbit);
1017 boost::iostreams::file_descriptor_source fd(
bfs::path(fname), std::ios_base::binary);
1020 if(!fd.is_open() && treat_failure_as_error) {
1021 ERR_FS <<
"Could not open '" << fname <<
"' for reading.\n";
1022 }
else if(!is_filename_case_correct(fname, fd)) {
1023 ERR_FS <<
"Not opening '" << fname <<
"' due to case mismatch.\n";
1025 s->clear(std::ios_base::failbit);
1029 return std::make_unique<boost::iostreams::stream<boost::iostreams::file_descriptor_source>>(fd, 4096, 0);
1030 }
catch(
const std::exception&) {
1031 if(treat_failure_as_error) {
1032 ERR_FS <<
"Could not open '" << fname <<
"' for reading.\n";
1036 s->clear(std::ios_base::failbit);
1043 LOG_FS <<
"streaming " << fname <<
" for writing.\n";
1046 boost::iostreams::file_descriptor_sink fd(
bfs::path(fname), std::ios_base::binary);
1047 return std::make_unique<boost::iostreams::stream<boost::iostreams::file_descriptor_sink>>(fd, 4096, 0);
1048 }
catch(
const BOOST_IOSTREAMS_FAILURE&
e) {
1051 error_code ec_unused;
1052 if(create_directory && bfs::create_directories(
bfs::path(fname).parent_path(), ec_unused)) {
1059 return new bfs::ofstream(
bfs::path(fname), std::ios_base::binary);
1064 void write_file(
const std::string& fname,
const std::string& data)
1067 os->exceptions(std::ios_base::goodbit);
1069 const std::size_t block_size = 4096;
1070 char buf[block_size];
1072 for(std::size_t
i = 0;
i < data.size();
i += block_size) {
1073 const std::size_t bytes = std::min<std::size_t>(block_size, data.size() -
i);
1074 std::copy(data.begin() +
i, data.begin() +
i + bytes, buf);
1076 os->write(buf, bytes);
1078 throw io_exception(
"Error writing to file: '" + fname +
"'");
1106 std::time_t mtime = bfs::last_write_time(
bfs::path(fname), ec);
1108 LOG_FS <<
"Failed to read modification time of " << fname <<
": " << ec.message() <<
'\n';
1116 return bfs::path(filename).extension() ==
".gz";
1121 return bfs::path(filename).extension() ==
".bz2";
1129 LOG_FS <<
"Failed to read filesize of " << fname <<
": " << ec.message() <<
'\n';
1131 }
else if(size > INT_MAX) {
1141 uintmax_t size_sum = 0;
1143 for(bfs::recursive_directory_iterator
i(p), end;
i != end && !ec; ++
i) {
1144 if(bfs::is_regular_file(
i->path())) {
1150 LOG_FS <<
"Failed to read directorysize of " << pname <<
": " << ec.message() <<
'\n';
1152 }
else if(size_sum > INT_MAX) {
1159 std::string
base_name(
const std::string& file,
const bool remove_extension)
1161 if(!remove_extension) {
1162 return bfs::path(file).filename().string();
1170 return bfs::path(file).parent_path().string();
1183 p =
p.parent_path();
1188 }
while(ec && !
is_root(
p.string()));
1190 return ec ?
"" :
p.string();
1196 const std::string
s = std::string(1, c);
1197 return sep ==
bfs::path(s).make_preferred();
1202 return bfs::path::preferred_separator;
1209 const bfs::path&
p = bfs::canonical(path, ec);
1210 return ec ? false : !p.has_parent_path();
1233 const std::wstring& wpath =
bfs::path{path}.make_preferred().wstring();
1234 return PathIsRootW(wpath.c_str()) == TRUE;
1240 return bfs::path{path}.root_name().string();
1248 std::string
normalize_path(
const std::string& fpath,
bool normalize_separators,
bool resolve_dot_entries)
1255 bfs::path p = resolve_dot_entries ? bfs::canonical(fpath, ec) : bfs::absolute(fpath);
1261 if(normalize_separators) {
1262 return p.make_preferred().string();
1282 std::set<std::string> binary_paths;
1284 typedef std::map<std::string, std::vector<std::string>> paths_map;
1285 paths_map binary_paths_cache;
1291 if(binary_paths.empty()) {
1292 binary_paths.insert(
"");
1296 binary_paths_manager::binary_paths_manager()
1318 std::string
path = bp[
"path"].str();
1319 if(path.find(
"..") != std::string::npos) {
1320 ERR_FS <<
"Invalid binary path '" << path <<
"'\n";
1324 if(!path.empty() && path.back() !=
'/')
1326 if(binary_paths.count(path) == 0) {
1327 binary_paths.insert(path);
1335 binary_paths_cache.clear();
1337 for(
const std::string&
p :
paths_) {
1338 binary_paths.erase(
p);
1344 binary_paths_cache.clear();
1349 DBG_FS <<
"Looking for '" << filename_str <<
"'.\n";
1351 if(filename_str.empty()) {
1352 LOG_FS <<
" invalid filename\n";
1356 if(filename_str.find(
"..") != std::string::npos) {
1357 ERR_FS <<
"Illegal path '" << filename_str <<
"' (\"..\" not allowed).\n";
1361 if(filename_str.find(
'\\') != std::string::npos) {
1362 ERR_FS <<
"Illegal path '" << filename_str
1363 << R
"end(' ("\" not allowed, for compatibility with GNU/Linux and macOS).)end" << std::endl; 1370 ERR_FS <<
"Illegal path '" << filename_str <<
"' (blacklisted filename)." << std::endl;
1374 if(std::any_of(filepath.begin(), filepath.end(),
1376 ERR_FS <<
"Illegal path '" << filename_str <<
"' (blacklisted directory name)." << std::endl;
1389 const paths_map::const_iterator itor = binary_paths_cache.find(type);
1390 if(itor != binary_paths_cache.end()) {
1391 return itor->second;
1394 if(type.find(
"..") != std::string::npos) {
1396 ERR_FS <<
"Invalid WML type '" << type <<
"' for binary paths\n";
1397 static std::vector<std::string>
dummy;
1401 std::vector<std::string>& res = binary_paths_cache[
type];
1405 for(
const std::string&
path : binary_paths) {
1432 std::string::size_type pos = filename.rfind(
"../");
1433 if(pos != std::string::npos) {
1439 return std::string();
1447 DBG_FS <<
" checking '" << bp <<
"'\n";
1450 DBG_FS <<
" found at '" << bpath.string() <<
"'\n";
1451 if(result.empty()) {
1452 result = bpath.string();
1460 DBG_FS <<
" not found\n";
1467 return std::string();
1473 DBG_FS <<
" checking '" << bp <<
"'\n";
1475 DBG_FS <<
" found at '" << bpath.string() <<
"'\n";
1476 return bpath.string();
1480 DBG_FS <<
" not found\n";
1481 return std::string();
1487 return std::string();
1495 if(filename[0] ==
'~') {
1497 DBG_FS <<
" trying '" << result.string() <<
"'\n";
1498 }
else if(*fpath.begin() ==
".") {
1499 if(!current_dir.empty()) {
1511 DBG_FS <<
" not found\n";
1514 DBG_FS <<
" found: '" << result.string() <<
"'\n";
1517 return result.string();
1522 bfs::path::iterator fi = full.begin(), fe = full.end(), pi = prefix.begin(), pe = prefix.end();
1523 while(fi != fe && pi != pe && *fi == *pi) {
1544 if(!partial.empty()) {
1545 return "~" + partial.generic_string();
1549 if(!partial.empty()) {
1550 return partial.generic_string();
1560 if(full_path.empty()) {
1561 return full_path.generic_string();
1565 if(!partial.empty()) {
1566 return partial.generic_string();
1570 if(!partial.empty()) {
1571 return partial.generic_string();
1574 return full_path.generic_string();
1579 const std::string real_program_name(program_name
1594 const char* user_name = getenv(
"USERNAME");
1596 const char* user_name = getenv(
"USER");
1600 if(user_name !=
nullptr) {
1601 boost::replace_all(canonicalized, user_name,
"USER");
1604 return canonicalized;
1614 const std::size_t pos_ext = base.rfind(
".");
1616 std::string loc_base;
1617 if(pos_ext != std::string::npos) {
1618 loc_base = base.substr(0, pos_ext) + suff + base.substr(pos_ext);
1620 loc_base = base + suff;
1633 std::vector<std::string> langs =
utils::split(
_(
"language code for localized resources^en_US"));
1638 langs.push_back(
"en_US");
1639 for(
const std::string& lang : langs) {
1640 std::string loc_file = dir +
"/" +
"l10n" +
"/" + lang +
"/" + loc_base;
std::string get_binary_dir_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual directory of a given type or an empty string if the directory i...
void remove()
Removes a tip.
bool delete_directory(const std::string &dirname, const bool keep_pbl)
static void push_if_exists(std::vector< std::string > *vec, const bfs::path &file, bool full)
static const std::string & get_version_path_suffix()
std::string get_program_invocation(const std::string &program_name)
Returns the appropriate invocation for a Wesnoth-related binary, assuming that it is located in the s...
std::string get_next_filename(const std::string &name, const std::string &extension)
Get the next free filename using "name + number (3 digits) + extension" maximum 1000 files then start...
Interfaces for manipulating version numbers of engine, add-ons, etc.
bool delete_file(const std::string &filename)
bool looks_like_pbl(const std::string &file)
static l_noret error(LoadState *S, const char *why)
void set_user_data_dir(std::string newprefdir)
config_array_view child_range(config_key_type key) const
static bool file_exists(const bfs::path &fpath)
bool ends_with(const std::string &str, const std::string &suffix)
void finish_log_file_setup()
Relocates the stdout+stderr log file to the user data directory.
void set_major_version(unsigned int)
Sets the major version number.
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
static void init_binary_paths()
std::string get_binary_file_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual file of a given type or an empty string if the file isn't prese...
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
static std::string _(const char *str)
Definitions for the interface to Wesnoth Markup Language (WML).
void clear_binary_paths_cache()
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
static bool is_legal_file(const std::string &filename_str)
bool match_file(const std::string &name) const
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
bool create_directory_if_missing_recursive(const std::string &dirname)
Creates a recursive directory tree if it does not exist already.
bool create_directory_if_missing(const std::string &dirname)
Creates a directory if it does not exist already.
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
static bfs::path user_config_dir
void read(config &cfg, std::istream &in, abstract_validator *validator)
static bool is_directory_internal(const bfs::path &fpath)
std::string get_user_data_dir()
void write_file(const std::string &fname, const std::string &data)
Throws io_exception if an error occurs.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Populates 'files' with all the files and 'dirs' with all the directories in dir.
std::string root_name(const std::string &path)
Returns the name of the root device if included in the given path.
std::string nearest_extant_parent(const std::string &file)
Finds the nearest parent in existence for a file or directory.
unsigned int minor_version() const
Retrieves the minor version number (x2 in "x1.x2.x3").
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
std::unique_ptr< std::istream > scoped_istream
std::string get_short_wml_path(const std::string &filename)
Returns a short path to filename, skipping the (user) data directory.
std::string get_independent_image_path(const std::string &filename)
Returns an image path to filename for binary path-independent use in saved games. ...
static bfs::path user_data_dir
std::string sanitize_path(const std::string &path)
Sanitizes a path to remove references to the user's name.
std::string get_dir(const std::string &dir)
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
static int writer(lua_State *L, const void *b, size_t size, void *ud)
bool is_path_sep(char c)
Returns whether c is a path separator.
std::unique_ptr< std::ostream > scoped_ostream
bool is_gzip_file(const std::string &filename)
Returns true if the file ends with '.gz'.
std::string get_cache_dir()
const std::vector< std::string > & get_binary_paths(const std::string &type)
Returns a vector with all possible paths to a given type of binary, e.g.
std::string get_exe_dir()
Log file control routines for Windows.
void set_user_config_dir(const std::string &newconfigdir)
bool is_relative(const std::string &path)
Returns whether the path seems to be relative.
int dir_size(const std::string &pname)
Returns the sum of the sizes of the files contained in a directory.
std::string get_wml_location(const std::string &filename, const std::string ¤t_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
std::vector< std::string > paths_
An exception object used when an IO error occurs.
static map_location::DIRECTION s
bool make_directory(const std::string &dirname)
bool is_root(const std::string &path)
Returns whether the path is the root of the file hierarchy.
Declarations for File-IO.
std::string get_localized_path(const std::string &file, const std::string &suff)
Returns the localized version of the given filename, if it exists.
static int sort(lua_State *L)
const version_info wesnoth_version(VERSION)
rng * generator
This generator is automatically synced during synced context.
Represents version numbers.
char path_separator()
Returns the standard path separator for the current platform.
static bfs::path cache_dir
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
int file_size(const std::string &fname)
Returns the size of a file, or -1 if the file doesn't exist.
bool set_cwd(const std::string &dir)
std::string get_user_config_dir()
void set_paths(const game_config_view &cfg)
bool is_bzip2_file(const std::string &filename)
Returns true if the file ends with '.bz2'.
std::time_t file_modified_time(const std::string &fname)
Get the modification time of a file.
std::vector< std::string > split(const config_attribute_value &val)
static bool error_except_not_found(const error_code &ec)
static void set_user_config_path(bfs::path newconfig)
static bfs::path subtract_path(const bfs::path &full, const bfs::path &prefix)
Standard logging facilities (interface).
std::vector< other_version_dir > find_other_version_saves_dirs()
Searches for directories containing saves created by other versions of Wesnoth.
There are still moves and/or attacks possible, but the unit doesn't fit in the "unmoved" status...
bool match_dir(const std::string &name) const
A config object defines a single node in a WML file, with access to child nodes.
static lg::log_domain log_filesystem("filesystem")
const version_info min_savegame_version(MIN_SAVEGAME_VERSION)
static void setup_user_data_dir()
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
std::string::const_iterator iterator
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::string wesnoth_program_dir
static const bfs::path & get_user_data_path()
static const blacklist_pattern_list default_blacklist
filesystem::scoped_ostream ostream_file(const std::string &fname, bool create_directory)