00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "global.hpp"
00022
00023
00024
00025
00026
00027
00028
00029 #include <sys/stat.h>
00030
00031 #ifdef _WIN32
00032 #include "filesystem_win32.ii"
00033 #include <cctype>
00034 #else
00035 #include <unistd.h>
00036 #include <dirent.h>
00037 #include <libgen.h>
00038 #endif
00039
00040 #ifdef __BEOS__
00041 #include <Directory.h>
00042 #include <FindDirectory.h>
00043 #include <Path.h>
00044 BPath be_path;
00045 #endif
00046
00047
00048 #include <cerrno>
00049 #include <fstream>
00050 #include <iomanip>
00051 #include <set>
00052 #include <boost/algorithm/string.hpp>
00053
00054
00055 #include <cstring>
00056
00057 #include "config.hpp"
00058 #include "filesystem.hpp"
00059 #include "foreach.hpp"
00060 #include "game_config.hpp"
00061 #include "game_preferences.hpp"
00062 #include "log.hpp"
00063 #include "loadscreen.hpp"
00064 #include "scoped_resource.hpp"
00065 #include "serialization/string_utils.hpp"
00066 #include "version.hpp"
00067
00068 static lg::log_domain log_filesystem("filesystem");
00069 #define DBG_FS LOG_STREAM(debug, log_filesystem)
00070 #define LOG_FS LOG_STREAM(info, log_filesystem)
00071 #define WRN_FS LOG_STREAM(warn, log_filesystem)
00072 #define ERR_FS LOG_STREAM(err, log_filesystem)
00073
00074 namespace {
00075 const mode_t AccessMode = 00770;
00076
00077
00078 const std::string maincfg_filename = "_main.cfg";
00079 const std::string finalcfg_filename = "_final.cfg";
00080 const std::string initialcfg_filename = "_initial.cfg";
00081
00082 }
00083
00084 #ifdef __APPLE__
00085 #include <CoreFoundation/CoreFoundation.h>
00086 #include <CoreFoundation/CFString.h>
00087 #include <CoreFoundation/CFBase.h>
00088 #endif
00089
00090 bool ends_with(const std::string& str, const std::string& suffix)
00091 {
00092 return str.size() >= suffix.size() && std::equal(suffix.begin(),suffix.end(),str.end()-suffix.size());
00093 }
00094
00095 void get_files_in_dir(const std::string &directory,
00096 std::vector<std::string>* files,
00097 std::vector<std::string>* dirs,
00098 file_name_option mode,
00099 file_filter_option filter,
00100 file_reorder_option reorder,
00101 file_tree_checksum* checksum)
00102 {
00103
00104
00105
00106 #ifndef __AMIGAOS4__
00107 if(!directory.empty() && directory[0] != '/' && !game_config::path.empty()){
00108 std::string dir = game_config::path + "/" + directory;
00109 if(is_directory(dir)) {
00110 get_files_in_dir(dir,files,dirs,mode,filter,reorder,checksum);
00111 return;
00112 }
00113 }
00114 #endif
00115
00116 struct stat st;
00117
00118 if (reorder == DO_REORDER) {
00119 LOG_FS << "searching for _main.cfg in directory " << directory << '\n';
00120 std::string maincfg;
00121 if (directory.empty() || directory[directory.size()-1] == '/'
00122 #ifdef __AMIGAOS4__
00123 || (directory[directory.size()-1]==':')
00124 #endif
00125 )
00126 maincfg = directory + maincfg_filename;
00127 else
00128 maincfg = (directory + "/") + maincfg_filename;
00129
00130 if (::stat(maincfg.c_str(), &st) != -1) {
00131 LOG_FS << "_main.cfg found : " << maincfg << '\n';
00132 if (files != NULL) {
00133 if (mode == ENTIRE_FILE_PATH)
00134 files->push_back(maincfg);
00135 else
00136 files->push_back(maincfg_filename);
00137 }
00138 return;
00139 }
00140 }
00141
00142 DIR* dir = opendir(directory.c_str());
00143
00144 if(dir == NULL) {
00145 return;
00146 }
00147
00148 struct dirent* entry;
00149 while((entry = readdir(dir)) != NULL) {
00150 if(entry->d_name[0] == '.')
00151 continue;
00152 #ifdef __APPLE__
00153
00154
00155 char macname[MAXNAMLEN+1];
00156 CFStringRef cstr = CFStringCreateWithCString(NULL,
00157 entry->d_name,
00158 kCFStringEncodingUTF8);
00159 CFMutableStringRef mut_str = CFStringCreateMutableCopy(NULL,
00160 0, cstr);
00161 CFStringNormalize(mut_str, kCFStringNormalizationFormC);
00162 CFStringGetCString(mut_str,
00163 macname,sizeof(macname)-1,
00164 kCFStringEncodingUTF8);
00165 CFRelease(cstr);
00166 CFRelease(mut_str);
00167 const std::string basename = macname;
00168 #else
00169
00170 const std::string basename = entry->d_name;
00171 #endif
00172
00173 std::string fullname;
00174 if (directory.empty() || directory[directory.size()-1] == '/'
00175 #ifdef __AMIGAOS4__
00176 || (directory[directory.size()-1]==':')
00177 #endif
00178 )
00179 fullname = directory + basename;
00180 else
00181 fullname = directory + "/" + basename;
00182
00183 if (::stat(fullname.c_str(), &st) != -1) {
00184 if (S_ISREG(st.st_mode)) {
00185 if(filter == SKIP_PBL_FILES && looks_like_pbl(basename)) {
00186 continue;
00187 }
00188 if (files != NULL) {
00189 if (mode == ENTIRE_FILE_PATH)
00190 files->push_back(fullname);
00191 else
00192 files->push_back(basename);
00193 }
00194 if (checksum != NULL) {
00195 if(st.st_mtime > checksum->modified) {
00196 checksum->modified = st.st_mtime;
00197 }
00198 checksum->sum_size += st.st_size;
00199 checksum->nfiles++;
00200 }
00201 } else if (S_ISDIR(st.st_mode)) {
00202 if (filter == SKIP_MEDIA_DIR
00203 && (basename == "images"|| basename == "sounds"))
00204 continue;
00205
00206 if (reorder == DO_REORDER &&
00207 ::stat((fullname+"/"+maincfg_filename).c_str(), &st)!=-1 &&
00208 S_ISREG(st.st_mode)) {
00209 LOG_FS << "_main.cfg found : ";
00210 if (files != NULL) {
00211 if (mode == ENTIRE_FILE_PATH) {
00212 files->push_back(fullname + "/" + maincfg_filename);
00213 LOG_FS << fullname << "/" << maincfg_filename << '\n';
00214 } else {
00215 files->push_back(basename + "/" + maincfg_filename);
00216 LOG_FS << basename << "/" << maincfg_filename << '\n';
00217 }
00218 } else {
00219
00220 LOG_FS << fullname << "/" << maincfg_filename << " not used now but skip the directory \n";
00221 }
00222 } else if (dirs != NULL) {
00223 if (mode == ENTIRE_FILE_PATH)
00224 dirs->push_back(fullname);
00225 else
00226 dirs->push_back(basename);
00227 }
00228 }
00229 }
00230 }
00231
00232 closedir(dir);
00233
00234 if(files != NULL)
00235 std::sort(files->begin(),files->end());
00236
00237 if (dirs != NULL)
00238 std::sort(dirs->begin(),dirs->end());
00239
00240 if (files != NULL && reorder == DO_REORDER) {
00241
00242 for (unsigned int i = 0; i < files->size(); i++) {
00243 if (ends_with((*files)[i], "/" + finalcfg_filename)) {
00244 files->push_back((*files)[i]);
00245 files->erase(files->begin()+i);
00246 break;
00247 }
00248 }
00249
00250 int foundit = -1;
00251 for (unsigned int i = 0; i < files->size(); i++)
00252 if (ends_with((*files)[i], "/" + initialcfg_filename)) {
00253 foundit = i;
00254 break;
00255 }
00256 if (foundit > 0) {
00257 std::string initialcfg = (*files)[foundit];
00258 for (unsigned int i = foundit; i > 0; i--)
00259 (*files)[i] = (*files)[i-1];
00260 (*files)[0] = initialcfg;
00261 }
00262 }
00263 }
00264
00265 #ifdef __native_client__
00266
00267 std::string get_prefs_file()
00268 {
00269 return "/wesnoth-userdata/preferences";
00270 }
00271
00272 std::string get_save_index_file()
00273 {
00274 return "/wesnoth-userdata/save_index";
00275 }
00276
00277 std::string get_saves_dir()
00278 {
00279 const std::string dir_path = "/wesnoth-userdata/saves";
00280 return get_dir(dir_path);
00281 }
00282
00283 #else
00284
00285 std::string get_prefs_file()
00286 {
00287 return get_user_config_dir() + "/preferences";
00288 }
00289
00290 std::string get_default_prefs_file()
00291 {
00292 #ifdef HAS_RELATIVE_DEFPREF
00293 return game_config::path + "/" + game_config::default_preferences_path;
00294 #else
00295 return game_config::default_preferences_path;
00296 #endif
00297 }
00298
00299 std::string get_save_index_file()
00300 {
00301 return get_user_data_dir() + "/save_index";
00302 }
00303
00304 std::string get_saves_dir()
00305 {
00306 const std::string dir_path = get_user_data_dir() + "/saves";
00307 return get_dir(dir_path);
00308 }
00309 #endif
00310
00311 std::string get_addon_campaigns_dir()
00312 {
00313 const std::string dir_path = get_user_data_dir() + "/data/add-ons";
00314 return get_dir(dir_path);
00315 }
00316
00317 std::string get_intl_dir()
00318 {
00319 #ifdef _WIN32
00320 return get_cwd() + "/translations";
00321 #else
00322
00323 #ifdef USE_INTERNAL_DATA
00324 return get_cwd() + "/" LOCALEDIR;
00325 #endif
00326
00327 #if HAS_RELATIVE_LOCALEDIR
00328 std::string res = game_config::path + "/" LOCALEDIR;
00329 #else
00330 std::string res = LOCALEDIR;
00331 #endif
00332
00333 return res;
00334 #endif
00335 }
00336
00337 std::string get_screenshot_dir()
00338 {
00339 const std::string dir_path = get_user_data_dir() + "/screenshots";
00340 return get_dir(dir_path);
00341 }
00342
00343 std::string get_next_filename(const std::string& name, const std::string& extension)
00344 {
00345 std::string next_filename;
00346 int counter = 0;
00347
00348 do {
00349 std::stringstream filename;
00350
00351 filename << name;
00352 filename.width(3);
00353 filename.fill('0');
00354 filename.setf(std::ios_base::right);
00355 filename << counter << extension;
00356 counter++;
00357 next_filename = filename.str();
00358 } while(file_exists(next_filename) && counter < 1000);
00359 return next_filename;
00360 }
00361
00362
00363 std::string get_dir(const std::string& dir_path)
00364 {
00365 DIR* dir = opendir(dir_path.c_str());
00366 if(dir == NULL) {
00367 const int res = mkdir(dir_path.c_str(),AccessMode);
00368 if(res == 0) {
00369 dir = opendir(dir_path.c_str());
00370 } else {
00371 ERR_FS << "could not open or create directory: " << dir_path << '\n';
00372 }
00373 }
00374
00375 if(dir == NULL)
00376 return "";
00377
00378 closedir(dir);
00379
00380 return dir_path;
00381 }
00382
00383 bool make_directory(const std::string& path)
00384 {
00385 return (mkdir(path.c_str(),AccessMode) == 0);
00386 }
00387
00388 bool looks_like_pbl(const std::string& file)
00389 {
00390 return utils::wildcard_string_match(utils::lowercase(file), "*.pbl");
00391 }
00392
00393
00394
00395 bool delete_directory(const std::string& path, const bool keep_pbl)
00396 {
00397 bool ret = true;
00398 std::vector<std::string> files;
00399 std::vector<std::string> dirs;
00400
00401 get_files_in_dir(path, &files, &dirs, ENTIRE_FILE_PATH, keep_pbl ? SKIP_PBL_FILES : NO_FILTER);
00402
00403 if(!files.empty()) {
00404 for(std::vector<std::string>::const_iterator i = files.begin(); i != files.end(); ++i) {
00405 errno = 0;
00406 if(remove((*i).c_str()) != 0) {
00407 LOG_FS << "remove(" << (*i) << "): " << strerror(errno) << "\n";
00408 ret = false;
00409 }
00410 }
00411 }
00412
00413 if(!dirs.empty()) {
00414 for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
00415 if(!delete_directory(*j))
00416 ret = false;
00417 }
00418 }
00419
00420 errno = 0;
00421 #ifdef _WIN32
00422
00423 int (*remove)(const char*);
00424 if(is_directory(path))
00425 remove = rmdir;
00426 else
00427 remove = ::remove;
00428 #endif
00429 if(remove(path.c_str()) != 0) {
00430 LOG_FS << "remove(" << path << "): " << strerror(errno) << "\n";
00431 ret = false;
00432 }
00433 return ret;
00434 }
00435
00436 std::string get_cwd()
00437 {
00438 char buf[1024];
00439 const char* const res = getcwd(buf,sizeof(buf));
00440 if(res != NULL) {
00441 std::string str(res);
00442
00443 #ifdef _WIN32
00444 std::replace(str.begin(),str.end(),'\\','/');
00445 #endif
00446
00447 return str;
00448 } else {
00449 return "";
00450 }
00451 }
00452
00453 std::string get_exe_dir()
00454 {
00455 #ifndef _WIN32
00456 char buf[1024];
00457 size_t path_size = readlink("/proc/self/exe", buf, 1024);
00458 if(path_size == static_cast<size_t>(-1))
00459 return std::string();
00460 buf[path_size] = 0;
00461 return std::string(dirname(buf));
00462 #else
00463 return get_cwd();
00464 #endif
00465 }
00466
00467 bool create_directory_if_missing(const std::string& dirname)
00468 {
00469 if(is_directory(dirname)) {
00470 DBG_FS << "directory " << dirname << " exists, not creating\n";
00471 return true;
00472 } else if(file_exists(dirname)) {
00473 ERR_FS << "cannot create directory " << dirname << "; file exists\n";
00474 return false;
00475 }
00476 DBG_FS << "creating missing directory " << dirname << '\n';
00477 return make_directory(dirname);
00478 }
00479 bool create_directory_if_missing_recursive(const std::string& dirname)
00480 {
00481 DBG_FS<<"creating recursive directory: "<<dirname<<'\n';
00482 if (is_directory(dirname) == false && dirname.empty() == false)
00483 {
00484 std::string tmp_dirname = dirname;
00485
00486 while ((tmp_dirname[tmp_dirname.size()-1] == '/' ||
00487 tmp_dirname[tmp_dirname.size()-1] == '\\') &&
00488 !tmp_dirname.empty())
00489 {
00490 tmp_dirname.erase(tmp_dirname.size()-1);
00491 }
00492
00493
00494 size_t pos = tmp_dirname.rfind("/");
00495
00496
00497
00498 if (tmp_dirname.rfind('\\') != std::string::npos &&
00499 tmp_dirname.rfind('\\') > pos )
00500 pos = tmp_dirname.rfind('\\');
00501
00502 if (pos != std::string::npos)
00503 create_directory_if_missing_recursive(tmp_dirname.substr(0,pos));
00504
00505 return create_directory_if_missing(tmp_dirname);
00506 }
00507 return create_directory_if_missing(dirname);
00508 }
00509
00510 static std::string user_data_dir, user_config_dir, cache_dir;
00511
00512 static void setup_user_data_dir();
00513
00514 #ifndef _WIN32
00515 static const std::string& get_version_path_suffix()
00516 {
00517 static std::string suffix;
00518
00519
00520
00521
00522 if(suffix.empty()) {
00523 std::ostringstream s;
00524 s << game_config::wesnoth_version.major_version() << '.'
00525 << game_config::wesnoth_version.minor_version();
00526 suffix = s.str();
00527 }
00528
00529 return suffix;
00530 }
00531 #endif
00532
00533 void set_preferences_dir(std::string path)
00534 {
00535 #ifdef _WIN32
00536 if(path.empty()) {
00537 game_config::preferences_dir = get_cwd() + "/userdata";
00538 } else if (path.size() > 2 && path[1] == ':') {
00539
00540 game_config::preferences_dir = path;
00541 } else {
00542 BOOL (*SHGetSpecialFolderPath)(HWND, LPTSTR, int, BOOL);
00543 HMODULE module = LoadLibrary("shell32");
00544 SHGetSpecialFolderPath = (BOOL (*)(HWND, LPTSTR, int, BOOL))GetProcAddress(module, "SHGetSpecialFolderPathA");
00545 if(SHGetSpecialFolderPath) {
00546 LOG_FS << "Using SHGetSpecialFolderPath to find My Documents\n";
00547 char my_documents_path[MAX_PATH];
00548 if(SHGetSpecialFolderPath(NULL, my_documents_path, 5, 1)) {
00549 std::string mygames_path = std::string(my_documents_path) + "/" + "My Games";
00550 boost::algorithm::replace_all(mygames_path, std::string("\\"), std::string("/"));
00551 create_directory_if_missing(mygames_path);
00552 game_config::preferences_dir = mygames_path + "/" + path;
00553 } else {
00554 WRN_FS << "SHGetSpecialFolderPath failed\n";
00555 game_config::preferences_dir = get_cwd() + "/" + path;
00556 }
00557 } else {
00558 LOG_FS << "Failed to load SHGetSpecialFolderPath function\n";
00559 game_config::preferences_dir = get_cwd() + "/" + path;
00560 }
00561 }
00562
00563 #else
00564
00565 #ifdef PREFERENCES_DIR
00566 if (path.empty()) path = PREFERENCES_DIR;
00567 #endif
00568
00569 std::string path2 = ".wesnoth" + get_version_path_suffix();
00570
00571 #ifdef _X11
00572 const char *home_str = getenv("HOME");
00573
00574 if (path.empty()) {
00575 char const *xdg_data = getenv("XDG_DATA_HOME");
00576 if (!xdg_data || xdg_data[0] == '\0') {
00577 if (!home_str) {
00578 path = path2;
00579 goto other;
00580 }
00581 user_data_dir = home_str;
00582 user_data_dir += "/.local/share";
00583 } else user_data_dir = xdg_data;
00584 user_data_dir += "/wesnoth/";
00585 user_data_dir += get_version_path_suffix();
00586 create_directory_if_missing_recursive(user_data_dir);
00587 game_config::preferences_dir = user_data_dir;
00588 } else {
00589 other:
00590 std::string home = home_str ? home_str : ".";
00591
00592 if (path[0] == '/')
00593 game_config::preferences_dir = path;
00594 else
00595 game_config::preferences_dir = home + "/" + path;
00596 }
00597 #else
00598 if (path.empty()) path = path2;
00599
00600 #ifdef __AMIGAOS4__
00601 game_config::preferences_dir = "PROGDIR:" + path;
00602 #elif defined(__BEOS__)
00603 if (be_path.InitCheck() != B_OK) {
00604 BPath tpath;
00605 if (find_directory(B_USER_SETTINGS_DIRECTORY, &be_path, true) == B_OK) {
00606 be_path.Append("wesnoth");
00607 } else {
00608 be_path.SetTo("/boot/home/config/settings/wesnoth");
00609 }
00610 game_config::preferences_dir = be_path.Path();
00611 }
00612 #else
00613 const char* home_str = getenv("HOME");
00614 std::string home = home_str ? home_str : ".";
00615
00616 if (path[0] == '/')
00617 game_config::preferences_dir = path;
00618 else
00619 game_config::preferences_dir = home + std::string("/") + path;
00620 #endif
00621 #endif
00622
00623 #endif
00624 user_data_dir = game_config::preferences_dir;
00625 setup_user_data_dir();
00626 }
00627
00628 static void setup_user_data_dir()
00629 {
00630 #ifdef _WIN32
00631 _mkdir(user_data_dir.c_str());
00632 _mkdir((user_data_dir + "/editor").c_str());
00633 _mkdir((user_data_dir + "/editor/maps").c_str());
00634 _mkdir((user_data_dir + "/data").c_str());
00635 _mkdir((user_data_dir + "/data/add-ons").c_str());
00636 _mkdir((user_data_dir + "/saves").c_str());
00637 _mkdir((user_data_dir + "/persist").c_str());
00638 #elif defined(__BEOS__)
00639 BPath tpath;
00640 #define BEOS_CREATE_PREFERENCES_SUBDIR(subdir) \
00641 tpath = be_path; \
00642 tpath.Append(subdir); \
00643 create_directory(tpath.Path(), 0775);
00644
00645 BEOS_CREATE_PREFERENCES_SUBDIR("editor");
00646 BEOS_CREATE_PREFERENCES_SUBDIR("editor/maps");
00647 BEOS_CREATE_PREFERENCES_SUBDIR("data");
00648 BEOS_CREATE_PREFERENCES_SUBDIR("data/add-ons");
00649 BEOS_CREATE_PREFERENCES_SUBDIR("saves");
00650 BEOS_CREATE_PREFERENCES_SUBDIR("persist");
00651 #undef BEOS_CREATE_PREFERENCES_SUBDIR
00652 #else
00653 const std::string& dir_path = user_data_dir;
00654
00655 const bool res = create_directory_if_missing(dir_path);
00656
00657 DIR* const dir = res ? opendir(dir_path.c_str()) : NULL;
00658 if(dir == NULL) {
00659 ERR_FS << "could not open or create preferences directory at " << dir_path << '\n';
00660 return;
00661 }
00662 closedir(dir);
00663
00664
00665 create_directory_if_missing(dir_path + "/editor");
00666 create_directory_if_missing(dir_path + "/editor/maps");
00667 create_directory_if_missing(dir_path + "/data");
00668 create_directory_if_missing(dir_path + "/data/add-ons");
00669 create_directory_if_missing(dir_path + "/saves");
00670 create_directory_if_missing(dir_path + "/persist");
00671 #endif
00672 }
00673
00674 const std::string& get_user_data_dir()
00675 {
00676
00677
00678
00679 if (user_data_dir.empty())
00680 {
00681 if (game_config::preferences_dir.empty())
00682 set_preferences_dir(std::string());
00683 else {
00684 user_data_dir = game_config::preferences_dir;
00685 setup_user_data_dir();
00686 }
00687 }
00688 return user_data_dir;
00689 }
00690
00691 const std::string &get_user_config_dir()
00692 {
00693 if (user_config_dir.empty())
00694 {
00695 #if defined(_X11) && !defined(PREFERENCES_DIR)
00696 char const *xdg_config = getenv("XDG_CONFIG_HOME");
00697 if (!xdg_config || xdg_config[0] == '\0') {
00698 xdg_config = getenv("HOME");
00699 if (!xdg_config) {
00700 user_config_dir = get_user_data_dir();
00701 return user_config_dir;
00702 }
00703 user_config_dir = xdg_config;
00704 user_config_dir += "/.config";
00705 } else user_config_dir = xdg_config;
00706 user_config_dir += "/wesnoth";
00707 create_directory_if_missing_recursive(user_config_dir);
00708 #else
00709 user_config_dir = get_user_data_dir();
00710 #endif
00711 }
00712 return user_config_dir;
00713 }
00714
00715 const std::string &get_cache_dir()
00716 {
00717 if (cache_dir.empty())
00718 {
00719 #if defined(_X11) && !defined(PREFERENCES_DIR)
00720 char const *xdg_cache = getenv("XDG_CACHE_HOME");
00721 if (!xdg_cache || xdg_cache[0] == '\0') {
00722 xdg_cache = getenv("HOME");
00723 if (!xdg_cache) {
00724 cache_dir = get_dir(get_user_data_dir() + "/cache");
00725 return cache_dir;
00726 }
00727 cache_dir = xdg_cache;
00728 cache_dir += "/.cache";
00729 } else cache_dir = xdg_cache;
00730 cache_dir += "/wesnoth";
00731 create_directory_if_missing_recursive(cache_dir);
00732 #else
00733 cache_dir = get_dir(get_user_data_dir() + "/cache");
00734 #endif
00735 }
00736 return cache_dir;
00737 }
00738
00739 static std::string read_stream(std::istream& s)
00740 {
00741 std::stringstream ss;
00742 ss << s.rdbuf();
00743 return ss.str();
00744 }
00745
00746 std::istream *istream_file(const std::string &fname)
00747 {
00748 LOG_FS << "Streaming " << fname << " for reading.\n";
00749 if (fname.empty())
00750 {
00751 ERR_FS << "Trying to open file with empty name.\n";
00752 std::ifstream *s = new std::ifstream();
00753 s->clear(std::ios_base::failbit);
00754 return s;
00755 }
00756
00757 std::ifstream *s = new std::ifstream(fname.c_str(),std::ios_base::binary);
00758 if (s->is_open())
00759 return s;
00760 ERR_FS << "Could not open '" << fname << "' for reading.\n";
00761 return s;
00762
00763 }
00764
00765 std::string read_file(const std::string &fname)
00766 {
00767 scoped_istream s = istream_file(fname);
00768 return read_stream(*s);
00769 }
00770
00771 std::ostream *ostream_file(std::string const &fname)
00772 {
00773 LOG_FS << "streaming " << fname << " for writing.\n";
00774 return new std::ofstream(fname.c_str(), std::ios_base::binary);
00775 }
00776
00777
00778 void write_file(const std::string& fname, const std::string& data)
00779 {
00780
00781 const util::scoped_FILE file(fopen(fname.c_str(),"wb"));
00782 if(file.get() == NULL) {
00783 throw io_exception("Could not open file for writing: '" + fname + "'");
00784 }
00785
00786 const size_t block_size = 4096;
00787 char buf[block_size];
00788
00789 for(size_t i = 0; i < data.size(); i += block_size) {
00790 const size_t bytes = std::min<size_t>(block_size,data.size() - i);
00791 std::copy(data.begin() + i, data.begin() + i + bytes,buf);
00792 const size_t res = fwrite(buf,1,bytes,file.get());
00793 if(res != bytes) {
00794 throw io_exception("Error writing to file: '" + fname + "'");
00795 }
00796 }
00797 }
00798
00799
00800 std::string read_map(const std::string& name)
00801 {
00802 std::string res;
00803 std::string map_location = get_wml_location("maps/" + name);
00804 if(!map_location.empty()) {
00805 res = read_file(map_location);
00806 }
00807
00808 if (res.empty()) {
00809 res = read_file(get_user_data_dir() + "/editor/maps/" + name);
00810 }
00811
00812 return res;
00813 }
00814
00815 static bool is_directory_internal(const std::string& fname)
00816 {
00817 #ifdef _WIN32
00818 _finddata_t info;
00819 const long handle = _findfirst((fname + "/*").c_str(),&info);
00820 if(handle >= 0) {
00821 _findclose(handle);
00822 return true;
00823 } else {
00824 return false;
00825 }
00826
00827 #else
00828 struct stat dir_stat;
00829 if(::stat(fname.c_str(), &dir_stat) == -1) {
00830 return false;
00831 }
00832 return S_ISDIR(dir_stat.st_mode);
00833 #endif
00834 }
00835
00836 bool is_directory(const std::string& fname)
00837 {
00838 if(fname.empty()) {
00839 return false;
00840 }
00841 if(fname[0] != '/' && !game_config::path.empty()) {
00842 if(is_directory_internal(game_config::path + "/" + fname))
00843 return true;
00844 }
00845
00846 return is_directory_internal(fname);
00847 }
00848
00849 bool file_exists(const std::string& name)
00850 {
00851 #ifdef _WIN32
00852 struct stat st;
00853 return (::stat(name.c_str(), &st) == 0);
00854 #else
00855 struct stat st;
00856 return (::stat(name.c_str(), &st) != -1);
00857 #endif
00858 }
00859
00860 time_t file_create_time(const std::string& fname)
00861 {
00862 struct stat buf;
00863 if(::stat(fname.c_str(),&buf) == -1)
00864 return 0;
00865
00866 return buf.st_mtime;
00867 }
00868
00869
00870
00871
00872
00873
00874 bool is_gzip_file(const std::string& filename)
00875 {
00876 return (filename.length() > 3
00877 && filename.substr(filename.length() - 3) == ".gz");
00878 }
00879
00880 file_tree_checksum::file_tree_checksum()
00881 : nfiles(0), sum_size(0), modified(0)
00882 {}
00883
00884 file_tree_checksum::file_tree_checksum(const config& cfg) :
00885 nfiles (lexical_cast_default<size_t>(cfg["nfiles"])),
00886 sum_size(lexical_cast_default<size_t>(cfg["size"])),
00887 modified(lexical_cast_default<time_t>(cfg["modified"]))
00888 {
00889 }
00890
00891 void file_tree_checksum::write(config& cfg) const
00892 {
00893 cfg["nfiles"] = lexical_cast<std::string>(nfiles);
00894 cfg["size"] = lexical_cast<std::string>(sum_size);
00895 cfg["modified"] = lexical_cast<std::string>(modified);
00896 }
00897
00898 bool file_tree_checksum::operator==(const file_tree_checksum &rhs) const
00899 {
00900 return nfiles == rhs.nfiles && sum_size == rhs.sum_size &&
00901 modified == rhs.modified;
00902 }
00903
00904 static void get_file_tree_checksum_internal(const std::string& path, file_tree_checksum& res)
00905 {
00906
00907 std::vector<std::string> dirs;
00908 get_files_in_dir(path,NULL,&dirs, ENTIRE_FILE_PATH, SKIP_MEDIA_DIR, DONT_REORDER, &res);
00909 loadscreen::increment_progress();
00910
00911 for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
00912 get_file_tree_checksum_internal(*j,res);
00913 }
00914 }
00915
00916 const file_tree_checksum& data_tree_checksum(bool reset)
00917 {
00918 static file_tree_checksum checksum;
00919 if (reset)
00920 checksum.reset();
00921 if(checksum.nfiles == 0) {
00922 get_file_tree_checksum_internal("data/",checksum);
00923 get_file_tree_checksum_internal(get_user_data_dir() + "/data/",checksum);
00924 LOG_FS << "calculated data tree checksum: "
00925 << checksum.nfiles << " files; "
00926 << checksum.sum_size << " bytes\n";
00927 }
00928
00929 return checksum;
00930 }
00931
00932 int file_size(const std::string& fname)
00933 {
00934 struct stat buf;
00935 if(::stat(fname.c_str(),&buf) == -1)
00936 return -1;
00937
00938 return buf.st_size;
00939 }
00940
00941 std::string file_name(const std::string& file)
00942
00943 {
00944 #ifdef _WIN32
00945 static const std::string dir_separators = "\\/:";
00946 #else
00947 static const std::string dir_separators = "/";
00948 #endif
00949
00950 std::string::size_type pos = file.find_last_of(dir_separators);
00951
00952 if(pos == std::string::npos)
00953 return file;
00954 if(pos >= file.size()-1)
00955 return "";
00956
00957 return file.substr(pos+1);
00958 }
00959
00960 std::string directory_name(const std::string& file)
00961
00962 {
00963 #ifdef _WIN32
00964 static const std::string dir_separators = "\\/:";
00965 #else
00966 static const std::string dir_separators = "/";
00967 #endif
00968
00969 std::string::size_type pos = file.find_last_of(dir_separators);
00970
00971 if(pos == std::string::npos)
00972 return "";
00973
00974 return file.substr(0,pos+1);
00975 }
00976
00977 namespace {
00978
00979 std::set<std::string> binary_paths;
00980
00981 typedef std::map<std::string,std::vector<std::string> > paths_map;
00982 paths_map binary_paths_cache;
00983
00984 }
00985
00986 static void init_binary_paths()
00987 {
00988 if(binary_paths.empty()) {
00989 binary_paths.insert("");
00990 }
00991 }
00992
00993 binary_paths_manager::binary_paths_manager() : paths_()
00994 {}
00995
00996 binary_paths_manager::binary_paths_manager(const config& cfg) : paths_()
00997 {
00998 set_paths(cfg);
00999 }
01000
01001 binary_paths_manager::~binary_paths_manager()
01002 {
01003 cleanup();
01004 }
01005
01006 void binary_paths_manager::set_paths(const config& cfg)
01007 {
01008 cleanup();
01009 init_binary_paths();
01010
01011 foreach (const config &bp, cfg.child_range("binary_path"))
01012 {
01013 std::string path = bp["path"].str();
01014 if (path.find("..") != std::string::npos) {
01015 ERR_FS << "Invalid binary path '" << path << "'\n";
01016 continue;
01017 }
01018 if (!path.empty() && path[path.size()-1] != '/') path += "/";
01019 if(binary_paths.count(path) == 0) {
01020 binary_paths.insert(path);
01021 paths_.push_back(path);
01022 }
01023 }
01024 }
01025
01026 void binary_paths_manager::cleanup()
01027 {
01028 binary_paths_cache.clear();
01029
01030 for(std::vector<std::string>::const_iterator i = paths_.begin(); i != paths_.end(); ++i) {
01031 binary_paths.erase(*i);
01032 }
01033 }
01034
01035 void clear_binary_paths_cache()
01036 {
01037 binary_paths_cache.clear();
01038 }
01039
01040 const std::vector<std::string>& get_binary_paths(const std::string& type)
01041 {
01042 const paths_map::const_iterator itor = binary_paths_cache.find(type);
01043 if(itor != binary_paths_cache.end()) {
01044 return itor->second;
01045 }
01046
01047 if (type.find("..") != std::string::npos) {
01048
01049 ERR_FS << "Invalid WML type '" << type << "' for binary paths\n";
01050 static std::vector<std::string> dummy;
01051 return dummy;
01052 }
01053
01054 std::vector<std::string>& res = binary_paths_cache[type];
01055
01056 init_binary_paths();
01057
01058 foreach (const std::string &path, binary_paths)
01059 {
01060 res.push_back(get_user_data_dir() + "/" + path + type + "/");
01061
01062 if(!game_config::path.empty()) {
01063 res.push_back(game_config::path + "/" + path + type + "/");
01064 }
01065 }
01066
01067
01068 res.push_back(get_user_data_dir() + "/");
01069
01070 if(!game_config::path.empty())
01071 res.push_back(game_config::path+"/");
01072
01073 return res;
01074 }
01075
01076 std::string get_binary_file_location(const std::string& type, const std::string& filename)
01077 {
01078 DBG_FS << "Looking for '" << filename << "'.\n";
01079
01080 if (filename.empty()) {
01081 LOG_FS << " invalid filename (type: " << type <<")\n";
01082 return std::string();
01083 }
01084
01085
01086
01087 std::string::size_type pos = filename.rfind("../");
01088 if (pos != std::string::npos) {
01089 std::string nf = filename.substr(pos + 3);
01090 LOG_FS << "Illegal path '" << filename << "' replaced by '" << nf << "'\n";
01091 return get_binary_file_location(type, nf);
01092 }
01093
01094 if (filename.find("..") != std::string::npos) {
01095 ERR_FS << "Illegal path '" << filename << "' (\"..\" not allowed).\n";
01096 return std::string();
01097 }
01098
01099 foreach (const std::string &path, get_binary_paths(type))
01100 {
01101 const std::string file = path + filename;
01102 DBG_FS << " checking '" << path << "'\n";
01103 if(file_exists(file)) {
01104 DBG_FS << " found at '" << file << "'\n";
01105 return file;
01106 }
01107 }
01108
01109 DBG_FS << " not found\n";
01110 return std::string();
01111 }
01112
01113 std::string get_binary_dir_location(const std::string &type, const std::string &filename)
01114 {
01115 DBG_FS << "Looking for '" << filename << "'.\n";
01116
01117 if (filename.empty()) {
01118 LOG_FS << " invalid filename (type: " << type <<")\n";
01119 return std::string();
01120 }
01121
01122 if (filename.find("..") != std::string::npos) {
01123 ERR_FS << "Illegal path '" << filename << "' (\"..\" not allowed).\n";
01124 return std::string();
01125 }
01126
01127 foreach (const std::string &path, get_binary_paths(type))
01128 {
01129 const std::string file = path + filename;
01130 DBG_FS << " checking '" << path << "'\n";
01131 if (is_directory(file)) {
01132 DBG_FS << " found at '" << file << "'\n";
01133 return file;
01134 }
01135 }
01136
01137 DBG_FS << " not found\n";
01138 return std::string();
01139 }
01140
01141 std::string get_wml_location(const std::string &filename, const std::string ¤t_dir)
01142 {
01143 DBG_FS << "Looking for '" << filename << "'.\n";
01144
01145 std::string result;
01146
01147 if (filename.empty()) {
01148 LOG_FS << " invalid filename\n";
01149 return result;
01150 }
01151
01152 if (filename.find("..") != std::string::npos) {
01153 ERR_FS << "Illegal path '" << filename << "' (\"..\" not allowed).\n";
01154 return result;
01155 }
01156
01157 bool already_found = false;
01158
01159 if (filename[0] == '~')
01160 {
01161
01162 result = get_user_data_dir() + "/data/" + filename.substr(1);
01163 DBG_FS << " trying '" << result << "'\n";
01164
01165 already_found = file_exists(result) || is_directory(result);
01166 }
01167 else if (filename.size() >= 2 && filename[0] == '.' && filename[1] == '/')
01168 {
01169
01170
01171 result = current_dir + filename.substr(2);
01172 }
01173 else if (!game_config::path.empty())
01174 result = game_config::path + "/data/" + filename;
01175
01176 DBG_FS << " trying '" << result << "'\n";
01177
01178 if (result.empty() ||
01179 (!already_found && !file_exists(result) && !is_directory(result)))
01180 {
01181 DBG_FS << " not found\n";
01182 result.clear();
01183 }
01184 else
01185 DBG_FS << " found: '" << result << "'\n";
01186
01187 return result;
01188 }
01189
01190 std::string get_short_wml_path(const std::string &filename)
01191 {
01192 std::string match = get_user_data_dir() + "/data/";
01193 if (filename.find(match) == 0) {
01194 return "~" + filename.substr(match.size());
01195 }
01196 match = game_config::path + "/data/";
01197 if (filename.find(match) == 0) {
01198 return filename.substr(match.size());
01199 }
01200 return filename;
01201 }
01202
01203 std::string get_independent_image_path(const std::string &filename)
01204 {
01205 std::string full_path = get_binary_file_location("images", filename);
01206
01207 if(!full_path.empty()) {
01208 std::string match = get_user_data_dir() + "/";
01209 if(full_path.find(match) == 0) {
01210 return full_path.substr(match.size());
01211 }
01212 match = game_config::path + "/";
01213 if(full_path.find(match) == 0) {
01214 return full_path.substr(match.size());
01215 }
01216 }
01217
01218 return full_path;
01219 }
01220
01221 std::string get_program_invocation(const std::string& program_name) {
01222 #ifdef DEBUG
01223 #ifdef _WIN32
01224 const char *program_suffix = "-debug.exe";
01225 #else
01226 const char *program_suffix = "-debug";
01227 #endif
01228 #else
01229 #ifdef _WIN32
01230 const char *program_suffix = ".exe";
01231 #else
01232 const char *program_suffix = "";
01233 #endif
01234 #endif
01235
01236 const std::string real_program_name(program_name + program_suffix);
01237 if(game_config::wesnoth_program_dir.empty()) return real_program_name;
01238 #ifdef _WIN32
01239 return game_config::wesnoth_program_dir + "\\" + real_program_name;
01240 #else
01241 return game_config::wesnoth_program_dir + "/" + real_program_name;
01242 #endif
01243 }
01244
01245 static bool is_path_sep(char c)
01246 {
01247 #ifdef _WIN32
01248 if (c == '/' || c == '\\') return true;
01249 #else
01250 if (c == '/') return true;
01251 #endif
01252 return false;
01253 }
01254
01255 std::string normalize_path(const std::string &p1)
01256 {
01257 if (p1.empty()) return p1;
01258
01259 std::string p2;
01260 #ifdef _WIN32
01261 if (p1.size() >= 2 && p1[1] == ':')
01262
01263 p2 = p1;
01264 else
01265 #endif
01266 if (!is_path_sep(p1[0]))
01267 p2 = get_cwd() + "/" + p1;
01268 else
01269 p2 = p1;
01270
01271 #ifdef _WIN32
01272 std::string drive;
01273 if (p2.size() >= 2 && p2[1] == ':') {
01274 drive = p2.substr(0, 2);
01275 p2.erase(0, 2);
01276 }
01277 #endif
01278
01279 std::vector<std::string> components(1);
01280 for (int i = 0, i_end = p2.size(); i <= i_end; ++i)
01281 {
01282 std::string &last = components[components.size() - 1];
01283 char c = p2.c_str()[i];
01284 if (is_path_sep(c) || c == 0)
01285 {
01286 if (last == ".")
01287 last.clear();
01288 else if (last == "..")
01289 {
01290 if (components.size() >= 2) {
01291 components.pop_back();
01292 components[components.size() - 1].clear();
01293 } else
01294 last.clear();
01295 }
01296 else if (!last.empty())
01297 components.push_back(std::string());
01298 }
01299 else
01300 last += c;
01301 }
01302
01303 std::ostringstream p4;
01304 components.pop_back();
01305
01306 #ifdef _WIN32
01307 p4 << drive;
01308 #endif
01309
01310 foreach (const std::string &s, components)
01311 {
01312 p4 << '/' << s;
01313 }
01314
01315 DBG_FS << "Normalizing '" << p2 << "' to '" << p4.str() << "'\n";
01316
01317 return p4.str();
01318 }
01319
01320 scoped_istream& scoped_istream::operator=(std::istream *s)
01321 {
01322 delete stream;
01323 stream = s;
01324 return *this;
01325 }
01326
01327 scoped_istream::~scoped_istream()
01328 {
01329 delete stream;
01330 }
01331
01332 scoped_ostream& scoped_ostream::operator=(std::ostream *s)
01333 {
01334 delete stream;
01335 stream = s;
01336 return *this;
01337 }
01338
01339 scoped_ostream::~scoped_ostream()
01340 {
01341 delete stream;
01342 }