20 #define GETTEXT_DOMAIN "wesnoth-lib"
34 #include <boost/algorithm/string/predicate.hpp>
35 #include <boost/algorithm/string/replace.hpp>
36 #include <boost/filesystem.hpp>
37 #include <boost/filesystem/fstream.hpp>
38 #include <boost/format.hpp>
39 #include <boost/iostreams/device/file_descriptor.hpp>
40 #include <boost/iostreams/stream.hpp>
42 #if !defined(_WIN32) && !defined(__APPLE__)
44 #if BOOST_VERSION >= 108600
45 #include <boost/process/v2/environment.hpp>
47 #include <boost/process/search_path.hpp>
53 #include <SDL2/SDL_system.h>
57 #include <boost/locale.hpp>
64 #ifndef VOLUME_NAME_NONE
65 #define VOLUME_NAME_NONE 0x4
71 #include <mach-o/dyld.h>
81 #if defined(__APPLE__) && defined(__MACH__) && defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
83 #define WESNOTH_BOOST_OS_IOS (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__*1000)
84 #include <SDL2/SDL_filesystem.h>
90 #define DBG_FS LOG_STREAM(debug, log_filesystem)
91 #define LOG_FS LOG_STREAM(info, log_filesystem)
92 #define WRN_FS LOG_STREAM(warn, log_filesystem)
93 #define ERR_FS LOG_STREAM(err, log_filesystem)
95 namespace bfs = boost::filesystem;
96 using boost::system::error_code;
104 std::string
path = WESNOTH_PATH;
109 #ifdef DEFAULT_PREFS_PATH
124 const std::string maincfg_filename =
"_main.cfg";
125 const std::string finalcfg_filename =
"_final.cfg";
126 const std::string initialcfg_filename =
"_initial.cfg";
129 class customcodecvt :
public std::codecvt<wchar_t , char , std::mbstate_t>
133 template<
typename char_t_to>
134 struct customcodecvt_do_conversion_writer
136 customcodecvt_do_conversion_writer(char_t_to*& _to_next, char_t_to* _to_end)
145 bool can_push(std::size_t count)
const
147 return static_cast<std::size_t
>(to_end - to_next) > count;
150 void push(char_t_to val)
152 assert(to_next != to_end);
157 template<
typename char_t_from,
typename char_t_to>
158 static void customcodecvt_do_conversion(std::mbstate_t& ,
159 const char_t_from* from,
160 const char_t_from* from_end,
161 const char_t_from*& from_next,
171 customcodecvt_do_conversion_writer<char_t_to> writer(to_next, to_end);
173 while(from_next != from_end) {
180 int do_encoding()
const noexcept
186 bool do_always_noconv()
const noexcept
191 int do_length(std::mbstate_t& ,
const char* ,
const char* , std::size_t )
const
194 throw "Not supported";
197 std::codecvt_base::result unshift(
198 std::mbstate_t& ,
char* ,
char* ,
char*& )
const
201 throw "Not supported";
205 std::codecvt_base::result do_in(std::mbstate_t& state,
207 const char* from_end,
208 const char*& from_next,
211 wchar_t*& to_next)
const
214 customcodecvt_do_conversion<char, wchar_t>(state, from, from_end, from_next, to, to_end, to_next);
217 return std::codecvt_base::error;
220 return std::codecvt_base::ok;
223 std::codecvt_base::result do_out(std::mbstate_t& state,
225 const wchar_t* from_end,
226 const wchar_t*& from_next,
229 char*& to_next)
const
232 customcodecvt_do_conversion<wchar_t, char>(state, from, from_end, from_next, to, to_end, to_next);
235 return std::codecvt_base::error;
238 return std::codecvt_base::ok;
252 utf8_loc = std::locale(utf8_loc,
new customcodecvt());
254 boost::filesystem::path::imbue(utf8_loc);
258 static static_runner static_bfs_path_imbuer;
260 bool is_filename_case_correct(
const std::string& fname,
const boost::iostreams::file_descriptor_source& fd)
262 wchar_t real_path[MAX_PATH];
263 GetFinalPathNameByHandleW(fd.handle(), real_path, MAX_PATH - 1, VOLUME_NAME_NONE);
270 bool is_filename_case_correct(
const std::string& ,
const boost::iostreams::file_descriptor_source& )
321 vec->push_back(file.generic_string());
323 vec->push_back(file.filename().generic_string());
330 return ec && ec != boost::system::errc::no_such_file_or_directory;
338 LOG_FS <<
"Failed to check if " << fpath.string() <<
" is a directory: " << ec.message();
349 ERR_FS <<
"Failed to check existence of file " << fpath.string() <<
": " << ec.message();
360 bfs::create_directory(dirpath, ec);
363 ERR_FS <<
"Failed to create directory " << dirpath.string() <<
": " << ec.message();
371 ERR_FS <<
"Could not open or create directory " << dirpath.string();
372 return std::string();
381 bfs::file_status
fs = bfs::status(dirpath, ec);
384 ERR_FS <<
"Failed to retrieve file status for " << dirpath.string() <<
": " << ec.message();
387 DBG_FS <<
"directory " << dirpath.string() <<
" exists, not creating";
390 ERR_FS <<
"cannot create directory " << dirpath.string() <<
"; file exists";
394 bool created = bfs::create_directory(dirpath, ec);
396 ERR_FS <<
"Failed to create directory " << dirpath.string() <<
": " << ec.message();
404 DBG_FS <<
"creating recursive directory: " << dirpath.string();
406 if(dirpath.empty()) {
411 bfs::file_status
fs = bfs::status(dirpath);
414 ERR_FS <<
"Failed to retrieve file status for " << dirpath.string() <<
": " << ec.message();
425 ERR_FS <<
"Could not create parents to " << dirpath.string();
433 while(fi != fe && pi != pe && *fi == *pi) {
467 std::vector<std::string>* files,
468 std::vector<std::string>* dirs,
487 LOG_FS <<
"searching for _main.cfg in directory " << dir;
488 const bfs::path maincfg = dirpath / maincfg_filename;
491 LOG_FS <<
"_main.cfg found : " << maincfg;
498 bfs::directory_iterator di(dirpath, ec);
499 bfs::directory_iterator end;
506 for(; di != end; ++di) {
508 bfs::file_status st = di->status(ec);
510 LOG_FS <<
"Failed to get file status of " << di->path().string() <<
": " << ec.message();
514 if(st.type() == bfs::regular_file) {
516 std::string basename = di->path().filename().string();
519 if(!basename.empty() && basename[0] ==
'.')
525 if(checksum !=
nullptr) {
532 LOG_FS <<
"Failed to read filesize of " << di->path().string() <<
": " << ec.message();
539 }
else if(st.type() == bfs::directory_file) {
540 std::string basename = di->path().filename().string();
542 if(!basename.empty() && basename[0] ==
'.') {
550 const bfs::path inner_main(di->path() / maincfg_filename);
551 bfs::file_status main_st = bfs::status(inner_main, ec);
554 LOG_FS <<
"Failed to get file status of " << inner_main.string() <<
": " << ec.message();
556 LOG_FS <<
"_main.cfg found : "
565 if(files !=
nullptr) {
566 std::sort(files->begin(), files->end());
569 if(dirs !=
nullptr) {
570 std::sort(dirs->begin(), dirs->end());
575 for(
unsigned int i = 0;
i < files->size();
i++) {
576 if(boost::algorithm::ends_with((*files)[
i],
"/" + finalcfg_filename)) {
577 files->push_back((*files)[
i]);
578 files->erase(files->begin() +
i);
585 for(
unsigned int i = 0;
i < files->size();
i++)
586 if(boost::algorithm::ends_with((*files)[
i],
"/" + initialcfg_filename)) {
591 std::string initialcfg = (*files)[foundit];
592 for(
unsigned int i = foundit;
i > 0;
i--)
593 (*files)[
i] = (*files)[
i - 1];
594 (*files)[0] = initialcfg;
606 std::string next_filename;
615 filename.setf(std::ios_base::right);
620 }
while(
file_exists(next_filename) && counter < 1000);
622 return next_filename;
634 std::ostringstream
s;
641 static std::string suffix;
653 #if defined(__APPLE__) && !defined(__IPHONEOS__)
659 static void migrate_apple_config_directory_for_unsandboxed_builds()
661 const char* home_str = getenv(
"HOME");
662 bfs::path home = home_str ? home_str :
".";
667 boost::filesystem::path new_saves_dir = home /
"Library/Containers/org.wesnoth.Wesnoth/Data/Library/Application Support/Wesnoth_";
672 LOG_FS <<
"Apple developer's userdata migration: symlinking " << old_saves_dir.string() <<
" to " << new_saves_dir.string();
673 bfs::create_symlink(new_saves_dir, old_saves_dir);
674 }
else if(!bfs::is_symlink(old_saves_dir)) {
675 ERR_FS <<
"Apple developer's userdata migration: Problem! Old (non-containerized) directory " << old_saves_dir.string() <<
" is not a symlink. Your savegames are scattered around 2 locations.";
685 #if defined(__APPLE__) && !defined(__IPHONEOS__)
686 migrate_apple_config_directory_for_unsandboxed_builds();
721 static utils::optional<std::string> get_games_path()
723 PWSTR docs_path =
nullptr;
724 HRESULT res = SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_CREATE,
nullptr, &docs_path);
725 utils::optional<std::string>
path = utils::nullopt;
729 path = games_path.string();
731 ERR_FS <<
"Could not determine path to user's Documents folder! (" << std::hex <<
"0x" << res << std::dec <<
") "
732 <<
"Please report this as a bug.";
735 CoTaskMemFree(docs_path);
742 #ifdef PREFERENCES_DIR
743 if(newprefdir.empty()) {
744 newprefdir = PREFERENCES_DIR;
745 DBG_FS <<
"Using PREFERENCES_DIR '" << PREFERENCES_DIR <<
"'";
751 if(newprefdir.empty()) {
754 #elif defined(__APPLE__)
756 #elif defined(WESNOTH_BOOST_OS_IOS)
757 char* sdl_pref_path = SDL_GetPrefPath(
"wesnoth.org",
"iWesnoth");
759 newprefdir = std::string(sdl_pref_path);
760 SDL_free(sdl_pref_path);
764 #elif defined(__ANDROID__)
765 newprefdir = SDL_AndroidGetExternalStoragePath();
767 const char*
h = std::getenv(
"HOME");
768 std::string home =
h ?
h :
"";
769 h = std::getenv(
"XDG_DATA_HOME");
770 std::string xdg_data_home =
h ?
h :
"";
771 if (!xdg_data_home.empty()) {
773 }
else if (!home.empty()) {
782 if(newprefdir[0] ==
'~') {
784 utils::optional<std::string> games_path = get_games_path();
790 WRN_FS <<
"Using current directory instead: " << dir.string();
793 const char*
h = std::getenv(
"HOME");
794 std::string home =
h ?
h :
"";
799 ERR_FS <<
"Unable to determine path to user's HOME.";
800 WRN_FS <<
"Using current directory instead: " << dir.string();
803 dir /= newprefdir.substr(1);
818 bool rename_dir(
const std::string& old_dir,
const std::string& new_dir)
821 bfs::rename(old_dir, new_dir, ec);
824 ERR_FS <<
"Failed to rename directory '" << old_dir <<
"' to '" << new_dir <<
"'";
834 ERR_FS <<
"could not open or create cache directory at " <<
cache_dir.string() <<
'\n';
845 assert(!
user_data_dir.empty() &&
"Attempted to access userdata location before userdata initialization!");
851 utils::optional<std::string> manual_path_opt;
853 boost::format manual_template(manual_dir +
"manual.%s.html");
854 bfs::path manual_path((manual_template % locale_code).str());
857 return "file://" + bfs::canonical(manual_path).string();
863 const auto& split_locale_code =
utils::split(locale_code,
'_');
864 const std::string& language_code = split_locale_code.empty() ?
"en" : split_locale_code[0];
865 manual_path = (manual_template % language_code).str();
869 return "file://" + bfs::canonical(manual_path).string();
888 #if defined(_X11) && !defined(PREFERENCES_DIR)
889 char const* xdg_cache = getenv(
"XDG_CACHE_HOME");
891 if(!xdg_cache || xdg_cache[0] ==
'\0') {
892 xdg_cache = getenv(
"HOME");
916 #if !defined(_WIN32) && !defined(_X11) && !defined(__APPLE__)
923 if(w_ver.major_version() != 1 || ms_ver.major_version() != 1) {
928 std::vector<other_version_dir> result;
931 for(
auto minor = w_ver.minor_version() + 4; minor >= ms_ver.minor_version(); --minor) {
932 if(minor == w_ver.minor_version())
937 version.set_minor_version(minor);
954 #elif defined(__APPLE__)
965 result.emplace_back(suffix,
path.string());
979 ERR_FS <<
"Failed to get current directory: " << ec.message();
983 return cwd.generic_string();
992 ERR_FS <<
"Failed to set current directory: " << ec.message();
995 LOG_FS <<
"Process working directory set to " << dir;
1004 wchar_t process_path[MAX_PATH];
1005 SetLastError(ERROR_SUCCESS);
1007 GetModuleFileNameW(
nullptr, process_path, MAX_PATH);
1009 if(GetLastError() != ERROR_SUCCESS) {
1010 return get_cwd() +
"/wesnoth";
1014 return exe.string();
1015 #elif defined(__APPLE__)
1016 std::vector<char> buffer(PATH_MAX, 0);
1017 uint32_t
size = PATH_MAX;
1018 if(_NSGetExecutablePath(&buffer[0], &
size) == 0) {
1019 buffer.resize(
size+1);
1020 return std::string(buffer.begin(), buffer.end());
1022 ERR_FS <<
"Path to wesnoth executable is too long";
1023 return get_cwd() +
"/The Battle for Wesnoth";
1030 bfs::path exe = bfs::read_symlink(self_exe, ec);
1032 return exe.string();
1040 #if BOOST_VERSION >= 108600
1041 bfs::path search = boost::process::v2::environment::find_executable(exe);
1043 bfs::path search = boost::process::search_path(exe);
1045 if(!search.empty()) {
1046 return search.string();
1051 #if BOOST_VERSION >= 108600
1052 search = boost::process::v2::environment::find_executable(exe);
1054 search = boost::process::search_path(exe);
1056 if(!search.empty()) {
1057 return search.string();
1061 return get_cwd() +
"/wesnoth";
1068 return path.parent_path().string();
1077 std::string
wesnothd = exe_dir +
"/wesnothd" + exe_name.substr(7);
1087 bool created = bfs::create_directory(
bfs::path(dirname), ec);
1089 ERR_FS <<
"Failed to create directory " << dirname <<
": " << ec.message();
1098 std::vector<std::string> files;
1099 std::vector<std::string> dirs;
1104 if(!files.empty()) {
1105 for(
const std::string&
f : files) {
1108 LOG_FS <<
"remove(" <<
f <<
"): " << ec.message();
1115 for(
const std::string&
d : dirs) {
1126 LOG_FS <<
"remove(" << dirname <<
"): " << ec.message();
1139 ERR_FS <<
"Could not delete file " <<
filename <<
": " << ec.message();
1147 std::ifstream file(fname, std::ios::binary);
1148 std::vector<uint8_t> file_contents;
1150 file_contents.reserve(
file_size(fname));
1151 file_contents.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
1153 return file_contents;
1160 std::string
img =
"";
1162 if(name.find(
".") != std::string::npos) {
1164 img =
"data:image/" + name.substr(name.find(
".") + 1) +
";base64," +
base64::encode(file_contents);
1173 std::stringstream ss;
1180 LOG_FS <<
"Streaming " << fname <<
" for reading.";
1183 ERR_FS <<
"Trying to open file with empty name.";
1185 s->clear(std::ios_base::failbit);
1192 boost::iostreams::file_descriptor_source fd(
bfs::path(fname), std::ios_base::binary);
1195 if(!fd.is_open() && treat_failure_as_error) {
1196 ERR_FS <<
"Could not open '" << fname <<
"' for reading.";
1197 }
else if(!is_filename_case_correct(fname, fd)) {
1198 ERR_FS <<
"Not opening '" << fname <<
"' due to case mismatch.";
1200 s->clear(std::ios_base::failbit);
1204 return std::make_unique<boost::iostreams::stream<boost::iostreams::file_descriptor_source>>(fd, 4096, 0);
1205 }
catch(
const std::exception&) {
1206 if(treat_failure_as_error) {
1207 ERR_FS <<
"Could not open '" << fname <<
"' for reading.";
1211 s->clear(std::ios_base::failbit);
1218 LOG_FS <<
"streaming " << fname <<
" for writing.";
1220 boost::iostreams::file_descriptor_sink fd(
bfs::path(fname), mode);
1221 return std::make_unique<boost::iostreams::stream<boost::iostreams::file_descriptor_sink>>(fd, 4096, 0);
1222 }
catch(
const BOOST_IOSTREAMS_FAILURE&
e) {
1225 error_code ec_unused;
1226 if(create_directory && bfs::create_directories(
bfs::path(fname).parent_path(), ec_unused)) {
1235 void write_file(
const std::string& fname,
const std::string&
data, std::ios_base::openmode mode)
1238 os->exceptions(std::ios_base::goodbit);
1240 const std::size_t block_size = 4096;
1241 char buf[block_size];
1243 for(std::size_t
i = 0;
i <
data.size();
i += block_size) {
1244 const std::size_t bytes = std::min<std::size_t>(block_size,
data.size() -
i);
1245 std::copy(
data.begin() +
i,
data.begin() +
i + bytes, buf);
1247 os->write(buf, bytes);
1249 throw io_exception(
"Error writing to file: '" + fname +
"'");
1283 std::time_t mtime = bfs::last_write_time(
path, ec);
1285 LOG_FS <<
"Failed to read modification time of " <<
path.string() <<
": " << ec.message();
1326 LOG_FS <<
"Failed to read filesize of " << fname <<
": " << ec.message();
1328 }
else if(
size > std::numeric_limits<int>::max()) {
1329 return std::numeric_limits<int>::max();
1338 uintmax_t size_sum = 0;
1340 for(bfs::recursive_directory_iterator
i(
p), end;
i != end && !ec; ++
i) {
1341 if(bfs::is_regular_file(
i->path())) {
1347 LOG_FS <<
"Failed to read directorysize of " << pname <<
": " << ec.message();
1349 }
else if(size_sum > std::numeric_limits<int>::max()) {
1350 return std::numeric_limits<int>::max();
1356 std::string
base_name(
const std::string& file,
const bool remove_extension)
1358 if(!remove_extension) {
1359 return bfs::path(file).filename().string();
1367 return bfs::path(file).parent_path().string();
1380 p =
p.parent_path();
1385 }
while(ec && !
is_root(
p.string()));
1387 return ec ?
"" :
p.string();
1393 const std::string
s = std::string(1,
c);
1399 return bfs::path::preferred_separator;
1407 return ec ? false : !
p.has_parent_path();
1430 const std::wstring wpath =
bfs::path{
path}.make_preferred().wstring();
1431 return PathIsRootW(wpath.c_str()) == TRUE;
1445 std::string
normalize_path(
const std::string& fpath,
bool normalize_separators,
bool resolve_dot_entries)
1452 bfs::path p = resolve_dot_entries ? bfs::canonical(fpath, ec) : bfs::absolute(fpath);
1458 if(normalize_separators) {
1459 return p.make_preferred().string();
1465 utils::optional<std::string>
to_asset_path(
const std::string&
path,
const std::string& addon_id,
const std::string& asset_type)
1469 bfs::path core_asset_dir = datadir /
"data" /
"core" / asset_type;
1470 bfs::path data_asset_dir = datadir / asset_type;
1478 outpath = bfs::relative(
path, core_asset_dir);
1483 outpath = bfs::relative(
path, data_asset_dir);
1485 }
else if(!addon_id.empty()) {
1487 addon_asset_dir /= asset_type;
1493 outpath = bfs::relative(
path, addon_asset_dir);
1498 return found ? utils::optional<std::string>{ outpath.string() } : utils::nullopt;
1515 std::set<std::string> binary_paths;
1517 typedef std::map<std::string, std::vector<std::string>> paths_map;
1518 paths_map binary_paths_cache;
1524 if(binary_paths.empty()) {
1525 binary_paths.insert(
"");
1547 std::string
path = bp[
"path"].str();
1548 if(
path.find(
"..") != std::string::npos) {
1549 ERR_FS <<
"Invalid binary path '" <<
path <<
"'";
1553 if(!
path.empty() &&
path.back() !=
'/')
1555 if(binary_paths.count(
path) == 0) {
1556 binary_paths.insert(
path);
1564 binary_paths_cache.clear();
1566 for(
const std::string&
p :
paths_) {
1567 binary_paths.erase(
p);
1573 binary_paths_cache.clear();
1578 DBG_FS <<
"Looking for '" << filename_str <<
"'.";
1580 if(filename_str.empty()) {
1581 LOG_FS <<
" invalid filename";
1585 if(filename_str.find(
"..") != std::string::npos) {
1586 ERR_FS <<
"Illegal path '" << filename_str <<
"' (\"..\" not allowed).";
1590 if(filename_str.find(
'\\') != std::string::npos) {
1591 ERR_FS <<
"Illegal path '" << filename_str
1592 << R
"end(' ("\" not allowed, for compatibility with GNU/Linux and macOS).)end";
1599 ERR_FS <<
"Illegal path '" << filename_str <<
"' (blacklisted filename).";
1603 if(std::any_of(filepath.begin(), filepath.end(),
1604 [](
const bfs::path& dirname) { return default_blacklist.match_dir(dirname.string()); })) {
1605 ERR_FS <<
"Illegal path '" << filename_str <<
"' (blacklisted directory name).";
1618 const paths_map::const_iterator itor = binary_paths_cache.find(
type);
1619 if(itor != binary_paths_cache.end()) {
1620 return itor->second;
1623 if(
type.find(
"..") != std::string::npos) {
1625 ERR_FS <<
"Invalid WML type '" <<
type <<
"' for binary paths";
1626 static std::vector<std::string>
dummy;
1630 std::vector<std::string>& res = binary_paths_cache[
type];
1634 for(
const std::string&
path : binary_paths) {
1661 std::string::size_type pos =
filename.rfind(
"../");
1662 if(pos != std::string::npos) {
1668 return utils::nullopt;
1675 const std::set<std::string> bpaths(temp.begin(), temp.end());
1676 for(
const std::string& bp : bpaths) {
1680 DBG_FS <<
" checking '" << bp <<
"'";
1683 DBG_FS <<
" found at '" << bpath.string() <<
"'";
1684 if(result.empty()) {
1685 result = bpath.string();
1687 WRN_FS <<
"Conflicting files in binary_path: '" << result
1688 <<
"' and '" << bpath.string() <<
"'";
1693 if(result.empty()) {
1695 return utils::nullopt;
1704 return utils::nullopt;
1710 DBG_FS <<
" checking '" << bp <<
"'";
1712 DBG_FS <<
" found at '" << bpath.string() <<
"'";
1713 return bpath.string();
1718 return utils::nullopt;
1721 utils::optional<std::string>
get_wml_location(
const std::string&
path,
const utils::optional<std::string>& current_dir)
1724 return utils::nullopt;
1730 if(
path[0] ==
'~') {
1732 DBG_FS <<
" trying '" << result.string() <<
"'";
1733 }
else if(*fpath.begin() ==
".") {
1735 WRN_FS <<
"Cannot resolve " <<
path <<
" since the current directory is unknown!";
1736 return utils::nullopt;
1742 WRN_FS <<
"Resolved path " <<
c <<
" is outside game and user data directories!";
1746 WRN_FS <<
"Cannot resolve " <<
path <<
" since the game data directory is unknown!";
1747 return utils::nullopt;
1754 return utils::nullopt;
1756 DBG_FS <<
" found: '" << result.string() <<
"'";
1757 return result.string();
1767 return "~" +
partial.generic_string();
1772 return partial.generic_string();
1782 return utils::nullopt;
1788 return partial.generic_string();
1793 return partial.generic_string();
1796 return full_path.generic_string();
1802 return program_name +
".exe";
1804 return program_name;
1811 const char* user_name = getenv(
"USERNAME");
1813 const char* user_name = getenv(
"USER");
1817 if(user_name !=
nullptr) {
1818 boost::replace_all(canonicalized, user_name,
"USER");
1821 return canonicalized;
1831 const std::size_t pos_ext = base.rfind(
".");
1833 std::string loc_base;
1834 if(pos_ext != std::string::npos) {
1835 loc_base = base.substr(0, pos_ext) + suff + base.substr(pos_ext);
1837 loc_base = base + suff;
1850 std::vector<std::string> langs =
utils::split(
_(
"language code for localized resources^en_US"));
1855 langs.push_back(
"en_US");
1856 for(
const std::string& lang : langs) {
1857 std::string loc_file = dir +
"/" +
"l10n" +
"/" + lang +
"/" + loc_base;
1863 return utils::nullopt;
1882 if(full_path.find(addons_path) == 0) {
1884 if(
path.size() > 0) {
1885 return path.begin()->string();
1889 return utils::nullopt;
A config object defines a single node in a WML file, with access to child nodes.
child_itors child_range(std::string_view key)
bool match_file(const std::string &name) const
A class grating read only view to a vector of config objects, viewed as one config with all children ...
Represents version numbers.
void set_major_version(unsigned int)
Sets the major version number.
unsigned int minor_version() const
Retrieves the minor version number (x2 in "x1.x2.x3").
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
Definitions for the interface to Wesnoth Markup Language (WML).
static lg::log_domain log_filesystem("filesystem")
Declarations for File-IO.
static std::string _(const char *str)
Standard logging facilities (interface).
std::string encode(utils::byte_view bytes)
auto parse_timestamp(long long val)
std::string get_legacy_editor_dir()
std::string get_cache_dir()
bool is_bzip2_file(const std::string &filename)
Returns true if the file ends with '.bz2'.
int dir_size(const std::string &pname)
Returns the sum of the sizes of the files contained in a directory.
static bfs::path subtract_path(const bfs::path &full, const bfs::path &prefix_path)
bool is_relative(const std::string &path)
Returns whether the path seems to be relative.
static const bfs::path & get_user_data_path()
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
bool is_root(const std::string &path)
Returns whether the path is the root of the file hierarchy.
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)
Get a list of all files and/or directories in a given directory.
const std::string wml_extension
std::chrono::system_clock::time_point file_modified_time(const bfs::path &path)
bool is_cfg(const std::string &filename)
Returns true if the file ends with the wmlfile extension.
static bfs::path get_dir(const bfs::path &dirpath)
std::string get_user_data_dir()
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
std::string get_wml_persist_dir()
bool rename_dir(const std::string &old_dir, const std::string &new_dir)
static bool is_legal_file(const std::string &filename_str)
std::string get_exe_path()
void copy_file(const std::string &src, const std::string &dest)
Read a file and then writes it back out.
bool delete_file(const std::string &filename)
bool is_gzip_file(const std::string &filename)
Returns true if the file ends with '.gz'.
static bool file_exists(const bfs::path &fpath)
std::string get_exe_dir()
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
static bool is_directory_internal(const bfs::path &fpath)
bool delete_directory(const std::string &dirname, const bool keep_pbl)
std::string get_synced_prefs_file()
location of preferences file containing preferences that are synced between computers note that wesno...
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > ¤t_dir)
Returns a translated path to the actual file or directory, if it exists.
std::string get_saves_dir()
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...
static bool is_prefix(const bfs::path &full, const bfs::path &prefix_path)
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::string get_unsynced_prefs_file()
location of preferences file containing preferences that aren't synced between computers
const std::string map_extension
utils::optional< 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, if it exists.
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
static bool create_directory_if_missing_recursive(const bfs::path &dirpath)
int file_size(const std::string &fname)
Returns the size of a file, or -1 if the file doesn't exist.
bool is_mask(const std::string &filename)
Returns true if the file ends with the maskfile extension.
utils::optional< std::string > get_addon_id_from_path(const std::string &location)
Returns the add-on ID from a path.
std::string read_file_as_data_uri(const std::string &fname)
void set_cache_dir(const std::string &newcachedir)
const std::string mask_extension
std::unique_ptr< std::istream > scoped_istream
void clear_binary_paths_cache()
static bfs::path user_data_dir
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode)
Throws io_exception if an error occurs.
std::string get_short_wml_path(const std::string &filename)
Returns a short path to filename, skipping the (user) data directory.
std::string root_name(const std::string &path)
Returns the name of the root device if included in the given path.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::string get_logs_dir()
std::unique_ptr< std::ostream > scoped_ostream
static bool create_directory_if_missing(const bfs::path &dirpath)
utils::optional< 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, if it exists.
bool is_userdata_initialized()
utils::optional< std::string > get_game_manual_file(const std::string &locale_code)
location of the game manual file correponding to the given locale (default: en)
std::string nearest_extant_parent(const std::string &file)
Finds the nearest parent in existence for a file or directory.
char path_separator()
Returns the standard path separator for the current platform.
std::string get_sync_dir()
parent directory for everything that should be synced between systems.
bool looks_like_pbl(const std::string &file)
std::string get_addons_data_dir()
static void push_if_exists(std::vector< std::string > *vec, const bfs::path &file, bool full)
bool is_map(const std::string &filename)
Returns true if the file ends with the mapfile extension.
bool make_directory(const std::string &dirname)
utils::optional< std::string > get_independent_binary_file_path(const std::string &type, const std::string &filename)
Returns an asset path to filename for binary path-independent use in saved games.
std::string get_wesnothd_name()
static void setup_user_data_dir()
const blacklist_pattern_list default_blacklist
std::string get_addons_dir()
bool set_cwd(const std::string &dir)
utils::optional< std::string > get_localized_path(const std::string &file, const std::string &suff)
Returns the localized version of the given filename, if it exists.
std::vector< other_version_dir > find_other_version_saves_dirs()
Searches for directories containing saves created by other versions of Wesnoth.
static bool check_prefix(bfs::path::iterator &fi, const bfs::path::iterator &fe, const bfs::path &prefix)
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...
static void set_cache_path(bfs::path newcache)
std::vector< uint8_t > read_file_binary(const std::string &fname)
std::string sanitize_path(const std::string &path)
Sanitizes a path to remove references to the user's name.
std::string get_current_editor_dir(const std::string &addon_id)
const std::string get_version_path_suffix(const version_info &version)
static bool error_except_not_found(const error_code &ec)
bool is_path_sep(char c)
Returns whether c is a path separator.
static bfs::path cache_dir
utils::optional< std::string > to_asset_path(const std::string &path, const std::string &addon_id, const std::string &asset_type)
Helper function to convert absolute path to wesnoth relative path.
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
void set_user_data_dir(std::string newprefdir)
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.
static void init_binary_paths()
Game configuration data as global variables.
const version_info min_savegame_version(MIN_SAVEGAME_VERSION)
std::string default_preferences_path
const version_info wesnoth_version(VERSION)
const std::string observer_team_name
observer team name used for observer team chat
int cache_compression_level
void remove()
Removes a tip.
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
config read(std::istream &in, abstract_validator *validator)
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
void move_log_file()
Move the log file to another directory.
std::string img(const std::string &src, const std::string &align, bool floating)
Generates a Help markup tag corresponding to an image.
rng * generator
This generator is automatically synced during synced context.
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
std::vector< std::string > split(const config_attribute_value &val)
std::string::const_iterator iterator
@ partial
There are still moves and/or attacks possible, but the unit doesn't fit in the "unmoved" status.
rect src
Non-transparent portion of the surface to compose.
std::string filename
Filename.
std::vector< std::string > paths_
binary_paths_manager()=default
void set_paths(const game_config_view &cfg)
std::chrono::system_clock::time_point modified
An exception object used when an IO error occurs.
static map_location::direction s