From a302f6c7ad187d84625f2ecc53b1c0d42bc848d8 Mon Sep 17 00:00:00 2001 From: Karol Date: Sun, 10 Aug 2014 23:42:39 +0200 Subject: [PATCH] VCMIDirs update #3 bfs = boost::filesystem; - Updateting filenames (std::string -> bfs::path) #1 - Added platform detection, and some specyfic boost::filesystem includes to Global.h - Updated CBasicLogConfigurator. Now class uses bfs::path pathes. --- Global.h | 53 ++++- client/CMT.cpp | 73 +++--- client/Client.cpp | 9 +- lib/CGameInterface.cpp | 7 +- lib/VCMIDirs.cpp | 317 ++++++++++++++------------ lib/VCMIDirs.h | 11 +- lib/filesystem/Filesystem.cpp | 12 +- lib/logging/CBasicLogConfigurator.cpp | 31 ++- lib/logging/CBasicLogConfigurator.h | 14 +- server/CVCMIServer.cpp | 2 +- 10 files changed, 303 insertions(+), 226 deletions(-) diff --git a/Global.h b/Global.h index baa9ee036..e7e5729c4 100644 --- a/Global.h +++ b/Global.h @@ -49,6 +49,50 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); # pragma warning (disable : 4800 ) /* disable conversion to bool warning -- I think it's intended in all places */ #endif +/* ---------------------------------------------------------------------------- */ +/* System detection. */ +/* ---------------------------------------------------------------------------- */ +// Based on: http://sourceforge.net/p/predef/wiki/OperatingSystems/ +// and on: http://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor +// TODO?: Should be moved to vstd\os_detect.h (and then included by Global.h) +#ifdef _WIN16 // Defined for 16-bit environments + #error "16-bit Windows isn't supported" +#elif defined(_WIN64) // Defined for 64-bit environments + #define VCMI_WINDOWS + #define VCMI_WINDOWS_64 +#elif defined(_WIN32) // Defined for both 32-bit and 64-bit environments + #define VCMI_WINDOWS + #define VCMI_WINDOWS_32 +#elif defined(_WIN32_WCE) + #error "Windows CE isn't supported" +#elif defined(__linux__) || defined(__gnu_linux__) || defined(linux) || defined(__linux) + #define VCMI_UNIX + #define VCMI_LINUX + #ifdef __ANDROID__ + #define VCMI_ANDROID + #endif +#elif defined(__APPLE__) && defined(__MACH__) + #define VCMI_UNIX + #define VCMI_APPLE + #include "TargetConditionals.h" + #if TARGET_IPHONE_SIMULATOR + #define VCMI_IOS + #define VCMI_IOS_SIM + #elif TARGET_OS_IPHONE + #define VCMI_IOS + #elif TARGET_OS_MAC + #define VCMI_MAC + #else + //#warning "Unknown Apple target."? + #endif +#else + #error "VCMI supports only Windows, OSX, Linux and Android targets" +#endif + +#ifdef VCMI_IOS + #error "iOS system isn't yet supported." +#endif + /* ---------------------------------------------------------------------------- */ /* Commonly used C++, Boost headers */ /* ---------------------------------------------------------------------------- */ @@ -99,6 +143,8 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); #include #include #include +#include +#include #include #include #include @@ -112,13 +158,12 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); #include #include - -#ifdef ANDROID -#include +#ifdef VCMI_ANDROID + #include #endif #ifndef M_PI -#define M_PI 3.14159265358979323846 + #define M_PI 3.14159265358979323846 #endif /* ---------------------------------------------------------------------------- */ diff --git a/client/CMT.cpp b/client/CMT.cpp index c424c0eb9..0af37c042 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -51,6 +51,7 @@ #endif namespace po = boost::program_options; +namespace bfs = boost::filesystem; /* * CMT.cpp, part of VCMI engine @@ -72,13 +73,12 @@ int preferredDriverIndex = -1; SDL_Window * mainWindow = nullptr; SDL_Renderer * mainRenderer = nullptr; SDL_Texture * screenTexture = nullptr; - #endif // VCMI_SDL1 extern boost::thread_specific_ptr inGuiThread; SDL_Surface *screen = nullptr, //main screen surface - *screen2 = nullptr,//and hlp surface (used to store not-active interfaces layer) + *screen2 = nullptr, //and hlp surface (used to store not-active interfaces layer) *screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed std::queue events; @@ -106,24 +106,23 @@ void endGame(); #include #endif -void startGameFromFile(const std::string &fname) +void startGameFromFile(const bfs::path &fname) { StartInfo si; try //attempt retrieving start info from given file { - if(!fname.size() || !boost::filesystem::exists(fname)) - throw std::runtime_error("Startfile \"" + fname + "\" does not exist!"); + if(fname.empty() || !bfs::exists(fname)) + throw std::runtime_error("Startfile \"" + fname.string() + "\" does not exist!"); - CLoadFile out(fname); - if(!out.sfile || !*out.sfile) - { - throw std::runtime_error("Cannot read from startfile \"" + fname + "\"!"); - } + // TODO: CLoadFile should take boost::path as an argument + CLoadFile out(fname.string()); + if (!out.sfile || !*out.sfile) + throw std::runtime_error("Cannot read from startfile \"" + fname.string() +"\"!"); out >> si; } catch(std::exception &e) { - logGlobal->errorStream() << "Failed to start from the file: " + fname << ". Error: " << e.what() + logGlobal->errorStream() << "Failed to start from the file: " << fname << ". Error: " << e.what() << " Falling back to main menu."; GH.curInt = CGPreGame::create(); return; @@ -218,7 +217,7 @@ int main(int argc, char** argv) ("help,h", "display help and exit") ("version,v", "display version information and exit") ("battle,b", po::value(), "runs game in duel mode (battle-only") - ("start", po::value(), "starts game from saved StartInfo file") + ("start", po::value(), "starts game from saved StartInfo file") ("onlyAI", "runs without human player, all players will be default AI") ("noGUI", "runs without GUI, implies --onlyAI") ("ai", po::value>(), "AI to be used for the player, can be specified several times for the consecutive players") @@ -271,15 +270,15 @@ int main(int argc, char** argv) CStopWatch total, pomtime; std::cout.flags(std::ios::unitbuf); console = new CConsoleHandler; - *console->cb = std::bind(&processCommand, _1); + *console->cb = processCommand; console->start(); atexit(dispose); - const auto logPath = VCMIDirs::get().userCachePath() + "/VCMI_Client_log.txt"; - CBasicLogConfigurator logConfig(logPath, console); + const bfs::path log_path = VCMIDirs::get().userCachePath() / "VCMI_Client_log.txt"; + CBasicLogConfigurator logConfig(log_path, console); logConfig.configureDefault(); logGlobal->infoStream() << "Creating console and configuring logger: " << pomtime.getDiff(); - logGlobal->infoStream() << "The log file will be saved to " << logPath; + logGlobal->infoStream() << "The log file will be saved to " << log_path; #ifdef __ANDROID__ // boost will crash without this @@ -430,15 +429,15 @@ int main(int argc, char** argv) session["autoSkip"].Bool() = vm.count("autoSkip"); session["oneGoodAI"].Bool() = vm.count("oneGoodAI"); - std::string fileToStartFrom; //none by default + bfs::path fileToStartFrom; //none by default if(vm.count("start")) - fileToStartFrom = vm["start"].as(); + fileToStartFrom = vm["start"].as(); - if(fileToStartFrom.size() && boost::filesystem::exists(fileToStartFrom)) + if(!fileToStartFrom.empty() && bfs::exists(fileToStartFrom)) startGameFromFile(fileToStartFrom); //ommit pregame and start the game using settings from file else { - if(fileToStartFrom.size()) + if(!fileToStartFrom.empty()) { logGlobal->warnStream() << "Warning: cannot find given file to start from (" << fileToStartFrom << "). Falling back to main menu."; @@ -584,7 +583,8 @@ void processCommand(const std::string &message) { std::cout<<"Command accepted.\t"; - std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/"; + const bfs::path out_path = + VCMIDirs::get().userCachePath() / "extracted"; auto list = CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident) { @@ -593,18 +593,19 @@ void processCommand(const std::string &message) for (auto & filename : list) { - std::string outName = outPath + filename.getName(); + const bfs::path file_path = out_path / (filename.getName() + ".TXT"); + std::string outName = file_path.string(); + + bfs::create_directories(file_path.parent_path()); - boost::filesystem::create_directories(outName.substr(0, outName.find_last_of("/"))); - - std::ofstream file(outName + ".TXT"); + bfs::ofstream file(file_path); auto text = CResourceHandler::get()->load(filename)->readAll(); file.write((char*)text.first.get(), text.second); } std::cout << "\rExtracting done :)\n"; - std::cout << " Extracted files can be found in " << outPath << " directory\n"; + std::cout << " Extracted files can be found in " << out_path << " directory\n"; } else if(cn=="crash") { @@ -710,15 +711,13 @@ void processCommand(const std::string &message) { CDefEssential * cde = CDefHandler::giveDefEss(URI); - std::string outName = URI; - std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/"; + const bfs::path out_path = VCMIDirs::get().userCachePath() / "extraced" / URI; + bfs::create_directories(out_path); - boost::filesystem::create_directories(outPath + outName); - - for (size_t i=0; iourImages.size(); i++) + for (size_t i = 0; i < cde->ourImages.size(); ++i) { - std::string filename = outPath + outName + '/' + boost::lexical_cast(i) + ".bmp"; - SDL_SaveBMP(cde->ourImages[i].bitmap, filename.c_str()); + const bfs::path file_path = out_path / (boost::lexical_cast(i) + ".bmp"); + SDL_SaveBMP(cde->ourImages[i].bitmap, file_path.string().c_str()); } } else @@ -731,14 +730,12 @@ void processCommand(const std::string &message) if (CResourceHandler::get()->existsResource(ResourceID(URI))) { - std::string outName = URI; - std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/"; - std::string fullPath = outPath + outName; + const bfs::path out_path = VCMIDirs::get().userCachePath() / "extracted" / URI; auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll(); - boost::filesystem::create_directories(fullPath.substr(0, fullPath.find_last_of("/"))); - std::ofstream outFile(outPath + outName, std::ofstream::binary); + bfs::create_directories(out_path.parent_path()); + bfs::ofstream outFile(out_path, bfs::ofstream::binary); outFile.write((char*)data.first.get(), data.second); } else diff --git a/client/Client.cpp b/client/Client.cpp index 528895cfd..01e139220 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -777,8 +777,8 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI) { if(ps.name.size()) { - std::string filename = VCMIDirs::get().libraryPath() + "/AI/" + VCMIDirs::get().libraryName(ps.name); - if(boost::filesystem::exists(filename)) + boost::filesystem::path ai_path = VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(ps.name); + if (boost::filesystem::exists(ai_path)) return ps.name; } @@ -865,8 +865,9 @@ CServerHandler::~CServerHandler() void CServerHandler::callServer() { setThreadName("CServerHandler::callServer"); - std::string logName = VCMIDirs::get().userCachePath() + "/server_log.txt"; - std::string comm = VCMIDirs::get().serverPath() + " --port=" + port + " > " + logName; + // TODO: Make more boost::filesystem::path based + const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string(); + const std::string comm = VCMIDirs::get().serverPath().string() + " --port=" + port + " > " + logName; int result = std::system(comm.c_str()); if (result == 0) logNetwork->infoStream() << "Server closed correctly"; diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index c438e0d55..5dc39bb6f 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -111,9 +111,10 @@ template shared_ptr createAnyAI(std::string dllname, std::string methodName) { logGlobal->infoStream() << "Opening " << dllname; - std::string filename = VCMIDirs::get().libraryName(dllname); - - auto ret = createAny(VCMIDirs::get().libraryPath() + "/AI/" + filename, methodName); + const boost::filesystem::path file_path = + VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(dllname); + // TODO: createAny Should take boost::filesystem::path in argument. + auto ret = createAny(file_path.string(), methodName); ret->dllName = dllname; return ret; } diff --git a/lib/VCMIDirs.cpp b/lib/VCMIDirs.cpp index d960b2a38..f780fb58e 100644 --- a/lib/VCMIDirs.cpp +++ b/lib/VCMIDirs.cpp @@ -11,11 +11,24 @@ #include "StdInc.h" #include "VCMIDirs.h" -namespace bfs = boost::filesystem; // Should be in each cpp file +namespace bfs = boost::filesystem; -#ifdef _WIN32 -// File: VCMIDirs_win32.h -//#include "IVCMIDirs.h" +bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; } + +void IVCMIDirs::init() +{ + // TODO: Log errors + bfs::create_directory(userDataPath()); + bfs::create_directory(userCachePath()); + bfs::create_directory(userConfigPath()); + bfs::create_directory(userSavePath()); +} + +#ifdef VCMI_WINDOWS + +#include +#include +#include class VCMIDirs_win32 : public IVCMIDirs { @@ -23,7 +36,6 @@ class VCMIDirs_win32 : public IVCMIDirs boost::filesystem::path userDataPath() const override; boost::filesystem::path userCachePath() const override; boost::filesystem::path userConfigPath() const override; - boost::filesystem::path userSavePath() const override; std::vector dataPaths() const override; @@ -36,23 +48,69 @@ class VCMIDirs_win32 : public IVCMIDirs std::string libraryName(const std::string& basename) const override; std::string genHelpString() const override; + + void init() override; }; -// End of file: VCMIDirs_win32.h -// File: VCMIDirs_win32.cpp -//#include "StdInc.h" -//#include "VCMIDirs_win32" -// WinAPI -#include // WideCharToMultiByte -#include // SHGetSpecialFolderPathW - -namespace VCMIDirs +void VCMIDirs_win32::init() { - const IVCMIDirs& get() + // Call base (init dirs) + IVCMIDirs::init(); + + auto moveDirIfExists = [](const bfs::path& from, const bfs::path& to) { - static VCMIDirs_win32 singleton; - return singleton; - } + if (!bfs::is_directory(from)) + return; // Nothing to do here. Flies away. + + if (bfs::is_empty(from)) + { + bfs::remove(from); + return; // Nothing to do here. Flies away. + } + + if (!bfs::is_directory(to)) + { + // IVCMIDirs::init() should create all destination directories. + // TODO: Log fact, that we shouldn't be here. + bfs::create_directory(to); + } + + // Why the hell path strings should be end with double null :/ + auto make_double_nulled = [](const bfs::path& path) -> std::unique_ptr + { + const std::wstring& path_str = path.native(); + std::unique_ptr result(new wchar_t[path_str.length() + 2]); + + size_t i = 0; + for (const wchar_t ch : path_str) + result[i++] = ch; + result[i++] = L'\0'; + result[i++] = L'\0'; + + return result; + }; + + auto from_dnulled = make_double_nulled(from / L"*.*"); + auto to_dnulled = make_double_nulled(to); + + SHFILEOPSTRUCTW file_op; + file_op.hwnd = GetConsoleWindow(); + file_op.wFunc = FO_MOVE; + file_op.pFrom = from_dnulled.get(); + file_op.pTo = to_dnulled.get(); + file_op.fFlags = 0; + file_op.hNameMappings = nullptr; + file_op.lpszProgressTitle = nullptr; + + const int error_code = SHFileOperationW(&file_op); + if (error_code != 0); // TODO: Log error. User should try to move files. + else if (file_op.fAnyOperationsAborted); // TODO: Log warn. User aborted operation. User should move files. + else if (!bfs::is_empty(from)); // TODO: Log warn. Some files not moved. User should try to move files. + else // TODO: Log fact that we moved files succefully. + bfs::remove(from); + }; + + moveDirIfExists(userDataPath() / "Games", userSavePath()); } bfs::path VCMIDirs_win32::userDataPath() const @@ -65,7 +123,12 @@ bfs::path VCMIDirs_win32::userDataPath() const // they should put their data under the locations referred to by CSIDL_APPDATA or CSIDL_LOCAL_APPDATA. if (SHGetSpecialFolderPathW(nullptr, profile_dir_w, CSIDL_PROFILE, FALSE) == FALSE) // WinAPI way failed { - // FIXME: Create macro for MS Visual Studio. + // FIXME: Use _wdupenv_s on MS Visual Studio. + // or: define _CRT_SECURE_NO_WARNINGS in preprocessor global settings. + // warning C4996: 'getenv': This function or variable may be unsafe. + // Consider using _dupenv_s instead. + // To disable deprecation, use _CRT_SECURE_NO_WARNINGS. + // See online help for details. if (profile_dir_a = std::getenv("userprofile")) // STL way succeed return bfs::path(profile_dir_a) / "vcmi"; else @@ -78,7 +141,6 @@ bfs::path VCMIDirs_win32::userDataPath() const } bfs::path VCMIDirs_win32::userCachePath() const { return userDataPath(); } bfs::path VCMIDirs_win32::userConfigPath() const { return userDataPath() / "config"; } -bfs::path VCMIDirs_win32::userSavePath() const { return userDataPath() / "Games"; } std::vector VCMIDirs_win32::dataPaths() const { @@ -93,61 +155,26 @@ bfs::path VCMIDirs_win32::binaryPath() const { return "."; } std::string VCMIDirs_win32::genHelpString() const { - // I think this function should have multiple versions - // 1. For various arguments - // 2. Inverse functions - // and should be moved to vstd - // or use http://utfcpp.sourceforge.net/ - auto utf8_convert = [](const bfs::path& path) -> std::string - { - const auto& path_string = path.native(); - auto perform_convert = [&path_string](LPSTR output, int output_size) - { - return WideCharToMultiByte( - CP_UTF8, // Use wchar_t -> utf8 char_t - WC_ERR_INVALID_CHARS, // Fails when invalid char occur - path_string.c_str(), // String to convert - path_string.size(), // String to convert size - output, // Result - output_size, // Result size - nullptr, nullptr); // For the ... CP_UTF8 settings for CodePage, this parameter must be set to NULL - }; - - int char_count = perform_convert(nullptr, 0); // Result size (0 - obtain size) - if (char_count > 0) - { - std::unique_ptr buffer(new char[char_count]); - if ((char_count = perform_convert(buffer.get(), char_count)) > 0) - return std::string(buffer.get(), char_count); - } - - // Conversion failed :C - return path.string(); - }; std::vector temp_vec; for (const bfs::path& path : dataPaths()) - temp_vec.push_back(utf8_convert(path)); - std::string gd_string_a = boost::algorithm::join(temp_vec, L";"); + temp_vec.push_back(path.string()); + std::string gd_string_a = boost::algorithm::join(temp_vec, ";"); return - " game data: " + gd_string_a + "\n" + - " libraries: " + utf8_convert(libraryPath()) + "\n" + - " server: " + utf8_convert(serverPath()) + "\n" + - "\n" + - " user data: " + utf8_convert(userDataPath()) + "\n" + - " user cache: " + utf8_convert(userCachePath()) + "\n" + - " user config: " + utf8_convert(userConfigPath()) + "\n" + - " user saves: " + utf8_convert(userSavePath()) + "\n"; // Should end without new-line? + " game data: " + gd_string_a + "\n" + " libraries: " + libraryPath().string() + "\n" + " server: " + serverPath().string() + "\n" + "\n" + " user data: " + userDataPath().string() + "\n" + " user cache: " + userCachePath().string() + "\n" + " user config: " + userConfigPath().string() + "\n" + " user saves: " + userSavePath().string() + "\n"; // Should end without new-line? } std::string VCMIDirs_win32::libraryName(const std::string& basename) const { return basename + ".dll"; } -// End of file: VCMIDirs_win32.cpp -#else // UNIX -// File: IVCMIDirs_UNIX.h -//#include "IVCMIDirs.h" - +#elif defined(VCMI_UNIX) class IVCMIDirs_UNIX : public IVCMIDirs { public: @@ -156,11 +183,6 @@ class IVCMIDirs_UNIX : public IVCMIDirs std::string genHelpString() const override; }; -// End of file: IVCMIDirs_UNIX.h - -// File: IVCMIDirs_UNIX.cpp -//#include "StdInc.h" -//#include "IVCMIDirs_UNIX.h" bfs::path IVCMIDirs_UNIX::clientPath() const { return binaryPath() / "vcmiclient"; } bfs::path IVCMIDirs_UNIX::clientPath() const { return binaryPath() / "vcmiserver"; } @@ -170,32 +192,27 @@ std::string IVCMIDirs_UNIX::genHelpString() const std::vector temp_vec; for (const bfs::path& path : dataPaths()) temp_vec.push_back(path.string()); - std::string gd_string_a = boost::algorithm::join(temp_vec, L";"); + std::string gd_string_a = boost::algorithm::join(temp_vec, ":"); return - " game data: " + gd_string_a + "\n" + - " libraries: " + libraryPath().string() + "\n" + - " server: " + serverPath().string() + "\n" + - "\n" + - " user data: " + userDataPath().string() + "\n" + - " user cache: " + userCachePath().string() + "\n" + - " user config: " + userConfigPath().string() + "\n" + + " game data: " + gd_string_a + "\n" + " libraries: " + libraryPath().string() + "\n" + " server: " + serverPath().string() + "\n" + "\n" + " user data: " + userDataPath().string() + "\n" + " user cache: " + userCachePath().string() + "\n" + " user config: " + userConfigPath().string() + "\n" " user saves: " + userSavePath().string() + "\n"; // Should end without new-line? } -// End of file: IVCMIDirs_UNIX.cpp - -#ifdef __APPLE__ -// File: VCMIDirs_OSX.h -//#include "IVCMIDirs_UNIX.h" +#ifdef VCMI_APPLE class VCMIDirs_OSX : public IVCMIDirs_UNIX { public: boost::filesystem::path userDataPath() const override; boost::filesystem::path userCachePath() const override; boost::filesystem::path userConfigPath() const override; - boost::filesystem::path userSavePath() const override; std::vector dataPaths() const override; @@ -203,20 +220,50 @@ class VCMIDirs_OSX : public IVCMIDirs_UNIX boost::filesystem::path binaryPath() const override; std::string libraryName(const std::string& basename) const override; + + void init() override; }; -// End of file: VCMIDirs_OSX.h -// File: VCMIDirs_OSX.cpp -//#include "StdInc.h" -//#include "VCMIDirs_OSX.h" - -namespace VCMIDirs +void VCMIDirs_OSX::init() { - const IVCMIDirs& get() + // Call base (init dirs) + IVCMIDirs_UNIX::init(); + + auto moveDirIfExists = [](const bfs::path& from, const bfs::path& to) { - static VCMIDirs_OSX singleton; - return singleton; - } + if (!bfs::is_directory(from)) + return; // Nothing to do here. Flies away. + + if (bfs::is_empty(from)) + { + bfs::remove(from); + return; // Nothing to do here. Flies away. + } + + if (!bfs::is_directory(to)) + { + // IVCMIDirs::init() should create all destination directories. + // TODO: Log fact, that we shouldn't be here. + bfs::create_directory(to); + } + + for (bfs::directory_iterator file(from); file != bfs::directory_iterator(); ++file) + { + const boost::filesystem::path& src_file_path = file->path(); + const boost::filesystem::path dst_file_path = to / src_file_path.filename(); + + // TODO: Aplication should ask user what to do when file exists: + // replace/ignore/stop process/replace all/ignore all + if (!boost::filesystem::exists(dst_file_path)) + bfs::rename(src_file_path, dst_file_path); + } + + if (!bfs::is_empty(from)); // TODO: Log warn. Some files not moved. User should try to move files. + else + bfs::remove(from); + }; + + moveDirIfExists(userDataPath() / "Games", userSavePath()); } bfs::path VCMIDirs_OSX::userDataPath() const @@ -234,7 +281,6 @@ bfs::path VCMIDirs_OSX::userDataPath() const } bfs::path VCMIDirs_OSX::userCachePath() const { return userDataPath(); } bfs::path VCMIDirs_OSX::userConfigPath() const { return userDataPath() / "config"; } -bfs::path VCMIDirs_OSX::userSavePath() const { return userDataPath() / "Games"; } std::vector VCMIDirs_OSX::dataPaths() const { @@ -245,18 +291,13 @@ bfs::path VCMIDirs_OSX::libraryPath() const { return "."; } bfs::path VCMIDirs_OSX::binaryPath() const { return "."; } std::string libraryName(const std::string& basename) { return "lib" + basename + ".dylib"; } -// End of file: VCMIDirs_OSX.cpp -#else -// File: VCMIDirs_Linux.h -//#include "IVCMIDirs_UNIX.h" - +#elif defined(VCMI_LINUX) class VCMIDirs_Linux : public IVCMIDirs_UNIX { public: boost::filesystem::path userDataPath() const override; boost::filesystem::path userCachePath() const override; boost::filesystem::path userConfigPath() const override; - boost::filesystem::path userSavePath() const override; std::vector dataPaths() const override; @@ -265,20 +306,6 @@ public: std::string libraryName(const std::string& basename) const override; }; -// End of file: VCMIDirs_Linux.h - -// File: VCMIDirs_Linux.cpp -//#include "StdInc.h" -//#include "VCMIDirs_Linux.h" - -namespace VCMIDirs -{ - const IVCMIDirs& get() - { - static VCMIDirs_Linux singleton; - return singleton; - } -} bfs::path VCMIDirs_Linux::userDataPath() const { @@ -313,10 +340,6 @@ bfs::path VCMIDirs_Linux::userConfigPath() const else return "."; } -bfs::path VCMIDirs_Linux::userSavePath() const -{ - return userDataPath() / "Saves"; -} std::vector VCMIDirs_Linux::dataPaths() const { @@ -328,8 +351,6 @@ std::vector VCMIDirs_Linux::dataPaths() const std::vector ret; const char* home_dir; - if (home_dir = getenv("HOME")) // compatibility, should be removed after 0.96 - ret.push_back(bfs::path(home_dir) / ".vcmi"); ret.push_back(M_DATA_DIR); if ((home_dir = getenv("XDG_DATA_DIRS")) != nullptr) @@ -353,46 +374,50 @@ bfs::path VCMIDirs_Linux::libraryPath() const { return M_LIB_PATH; } bfs::path VCMIDirs_Linux::binaryPath() const { return M_BIN_DIR; } std::string VCMIDirs_Linux::libraryName(const std::string& basename) const { return "lib" + basename + ".so"; } -// End of file VCMIDirs_Linux.cpp -#ifdef __ANDROID__ -// File: VCMIDirs_Android.h -//#include "VCMIDirs_Linux.h" +#ifdef VCMI_ANDROID class VCMIDirs_Android : public VCMIDirs_Linux { public: boost::filesystem::path userDataPath() const override; boost::filesystem::path userCachePath() const override; boost::filesystem::path userConfigPath() const override; - boost::filesystem::path userSavePath() const override; std::vector dataPaths() const override; }; -// End of file: VCMIDirs_Android.h - -// File: VCMIDirs_Android.cpp -//#include "StdInc.h" -//#include "VCMIDirs_Android.h" - -namespace VCMIDirs -{ - const IVCMIDirs& get() - { - static VCMIDirs_Android singleton; - return singleton; - } -} // on Android HOME will be set to something like /sdcard/data/Android/is.xyz.vcmi/files/ bfs::path VCMIDirs_Android::userDataPath() const { return getenv("HOME"); } bfs::path VCMIDirs_Android::userCachePath() const { return userDataPath() / "cache"; } bfs::path VCMIDirs_Android::userConfigPath() const { return userDataPath() / "config"; } -bfs::path VCMIDirs_Android::userSavePath() const { return userDataPath() / "Saves"; } std::vector VCMIDirs_Android::dataPaths() const { return std::vector(1, userDataPath()); } -// End of file: VCMIDirs_Android.cpp -#endif -#endif -#endif \ No newline at end of file +#endif // VCMI_ANDROID +#endif // VCMI_APPLE, VCMI_LINUX +#endif // VCMI_WINDOWS, VCMI_UNIX + +// Getters for interfaces are separated for clarity. +namespace VCMIDirs +{ + const IVCMIDirs& get() + { + #ifdef VCMI_WINDOWS + static VCMIDirs_win32 singleton; + #elif defined(VCMI_ANDROID) + static VCMIDirs_Android singleton; + #elif defined(VCMI_LINUX) + static VCMIDirs_Linux singleton; + #elif defined(VCMI_APPLE) + static VCMIDirs_OSX singleton; + #endif + static bool initialized = false; + if (!initialized) + { + singleton.init(); + initialized = true; + } + return singleton; + } +} \ No newline at end of file diff --git a/lib/VCMIDirs.h b/lib/VCMIDirs.h index f90c09ed9..37e3150af 100644 --- a/lib/VCMIDirs.h +++ b/lib/VCMIDirs.h @@ -16,9 +16,9 @@ // Boost #include -// TODO: File should be rename to IVCMIDirs.h +// TODO: File should be renamed to IVCMIDirs.h -class IVCMIDirs +class DLL_LINKAGE IVCMIDirs { public: // Path to user-specific data directory @@ -31,7 +31,7 @@ class IVCMIDirs virtual boost::filesystem::path userConfigPath() const = 0; // Path to saved games - virtual boost::filesystem::path userSavePath() const = 0; + virtual boost::filesystem::path userSavePath() const; // Paths to global system-wide data directories. First items have higher priority virtual std::vector dataPaths() const = 0; @@ -54,6 +54,11 @@ class IVCMIDirs // virtual std::string libraryName(std::string&& basename) const = 0;? virtual std::string genHelpString() const = 0; + + // Creates not existed, but required directories. + // Updates directories what change name/path between versions. + // Function called automatically. + virtual void init(); }; namespace VCMIDirs diff --git a/lib/filesystem/Filesystem.cpp b/lib/filesystem/Filesystem.cpp index ce49e7f9f..b34954ec4 100644 --- a/lib/filesystem/Filesystem.cpp +++ b/lib/filesystem/Filesystem.cpp @@ -69,7 +69,7 @@ void CFilesystemGenerator::loadDirectory(const std::string &mountPoint, const Js std::string URI = prefix + config["path"].String(); int depth = 16; if (!config["depth"].isNull()) - depth = config["depth"].Float(); + depth = (int)config["depth"].Float(); ResourceID resID(URI, EResType::DIRECTORY); @@ -136,12 +136,13 @@ ISimpleResourceLoader * CResourceHandler::createInitial() } }; + // TODO: CFilesystemLoader: Should take boost::filesystem::path in argument for (auto & path : VCMIDirs::get().dataPaths()) { if (boost::filesystem::is_directory(path)) // some of system-provided paths may not exist - initialLoader->addLoader(new CFilesystemLoader("", path, 0, true), false); + initialLoader->addLoader(new CFilesystemLoader("", path.string(), 0, true), false); } - initialLoader->addLoader(new CFilesystemLoader("", VCMIDirs::get().userDataPath(), 0, true), false); + initialLoader->addLoader(new CFilesystemLoader("", VCMIDirs::get().userDataPath().string(), 0, true), false); recurseInDir("CONFIG", 0);// look for configs recurseInDir("DATA", 0); // look for archives @@ -166,9 +167,10 @@ void CResourceHandler::initialize() // |-saves // |-config + // TODO: CFilesystemLoader should take boost::filesystem::path knownLoaders["root"] = new CFilesystemList(); - knownLoaders["saves"] = new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath()); - knownLoaders["config"] = new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath()); + knownLoaders["saves"] = new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath().string()); + knownLoaders["config"] = new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath().string()); auto localFS = new CFilesystemList(); localFS->addLoader(knownLoaders["saves"], true); diff --git a/lib/logging/CBasicLogConfigurator.cpp b/lib/logging/CBasicLogConfigurator.cpp index 2db783475..1d42c180d 100644 --- a/lib/logging/CBasicLogConfigurator.cpp +++ b/lib/logging/CBasicLogConfigurator.cpp @@ -3,16 +3,17 @@ #include "../CConfigHandler.h" -CBasicLogConfigurator::CBasicLogConfigurator(const std::string & filePath, CConsoleHandler * console) : filePath(filePath), - console(console), appendToLogFile(false) -{ +CBasicLogConfigurator::CBasicLogConfigurator(const boost::filesystem::path & file_path, CConsoleHandler * const console) : + file_path(file_path), console(console), appendToLogFile(false) {} -} +CBasicLogConfigurator::CBasicLogConfigurator(boost::filesystem::path && file_path, CConsoleHandler * const console) : + file_path(std::move(file_path)), console(console), appendToLogFile(false) {} void CBasicLogConfigurator::configureDefault() { CLogger::getGlobalLogger()->addTarget(make_unique(console)); - CLogger::getGlobalLogger()->addTarget(make_unique(filePath, appendToLogFile)); + // TODO: CLogFileTarget should take boost::filesystem::path as an argument + CLogger::getGlobalLogger()->addTarget(make_unique(file_path.string(), appendToLogFile)); appendToLogFile = true; } @@ -21,7 +22,8 @@ void CBasicLogConfigurator::configure() try { const JsonNode & loggingNode = settings["logging"]; - if(loggingNode.isNull()) throw std::runtime_error("Settings haven't been loaded."); + if(loggingNode.isNull()) + throw std::runtime_error("Settings haven't been loaded."); // Configure loggers const JsonNode & loggers = loggingNode["loggers"]; @@ -67,8 +69,9 @@ void CBasicLogConfigurator::configure() } CLogger::getGlobalLogger()->addTarget(std::move(consoleTarget)); + // TODO: CLogFileTarget should take boost::filesystem::path as an argument // Add file target - auto fileTarget = make_unique(filePath, appendToLogFile); + auto fileTarget = make_unique(file_path.string(), appendToLogFile); const JsonNode & fileNode = loggingNode["file"]; if(!fileNode.isNull()) { @@ -87,7 +90,7 @@ void CBasicLogConfigurator::configure() logGlobal->infoStream() << "Initialized logging system based on settings successfully."; } -ELogLevel::ELogLevel CBasicLogConfigurator::getLogLevel(const std::string & level) const +ELogLevel::ELogLevel CBasicLogConfigurator::getLogLevel(const std::string & level) { static const std::map levelMap = boost::assign::map_list_of ("trace", ELogLevel::TRACE) @@ -95,18 +98,15 @@ ELogLevel::ELogLevel CBasicLogConfigurator::getLogLevel(const std::string & leve ("info", ELogLevel::INFO) ("warn", ELogLevel::WARN) ("error", ELogLevel::ERROR); + const auto & levelPair = levelMap.find(level); if(levelPair != levelMap.end()) - { return levelPair->second; - } else - { throw std::runtime_error("Log level " + level + " unknown."); - } } -EConsoleTextColor::EConsoleTextColor CBasicLogConfigurator::getConsoleColor(const std::string & colorName) const +EConsoleTextColor::EConsoleTextColor CBasicLogConfigurator::getConsoleColor(const std::string & colorName) { static const std::map colorMap = boost::assign::map_list_of ("default", EConsoleTextColor::DEFAULT) @@ -117,13 +117,10 @@ EConsoleTextColor::EConsoleTextColor CBasicLogConfigurator::getConsoleColor(cons ("white", EConsoleTextColor::WHITE) ("gray", EConsoleTextColor::GRAY) ("teal", EConsoleTextColor::TEAL); + const auto & colorPair = colorMap.find(colorName); if(colorPair != colorMap.end()) - { return colorPair->second; - } else - { throw std::runtime_error("Color " + colorName + " unknown."); - } } diff --git a/lib/logging/CBasicLogConfigurator.h b/lib/logging/CBasicLogConfigurator.h index 4c42f2ea4..a9f98bfe4 100644 --- a/lib/logging/CBasicLogConfigurator.h +++ b/lib/logging/CBasicLogConfigurator.h @@ -22,7 +22,8 @@ class JsonNode; class DLL_LINKAGE CBasicLogConfigurator { public: - CBasicLogConfigurator(const std::string & filePath, CConsoleHandler * console); + CBasicLogConfigurator(const boost::filesystem::path & file_path, CConsoleHandler * const console); + CBasicLogConfigurator(boost::filesystem::path && file_path, CConsoleHandler * const console); /// Configures the logging system by parsing the logging settings. It adds the console target and the file target to the global logger. /// Doesn't throw, but logs on success or fault. @@ -30,12 +31,15 @@ public: /// Configures a default logging system by adding the console target and the file target to the global logger. void configureDefault(); - private: - ELogLevel::ELogLevel getLogLevel(const std::string & level) const; - EConsoleTextColor::EConsoleTextColor getConsoleColor(const std::string & colorName) const; + // Gets ELogLevel enum from string. (Should be moved to CLogger as a separate function?) + // Throws: std::runtime_error + static ELogLevel::ELogLevel getLogLevel(const std::string & level); + // Gets EConsoleTextColor enum from strings. (Should be moved to CLogger as a separate function?) + // Throws: std::runtime_error + static EConsoleTextColor::EConsoleTextColor getConsoleColor(const std::string & colorName); - std::string filePath; + boost::filesystem::path file_path; CConsoleHandler * console; bool appendToLogFile; }; diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 2d5ca8533..94dd46c7d 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -596,7 +596,7 @@ int main(int argc, char** argv) #endif console = new CConsoleHandler; - CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Server_log.txt", console); + CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Server_log.txt", console); logConfig.configureDefault(); preinitDLL(console);