From 958839668cc9a0702726967793b4ebd180c6e77c Mon Sep 17 00:00:00 2001 From: Karol Date: Thu, 21 Aug 2014 22:26:28 +0200 Subject: [PATCH] VCMIDirs update #5 - Minor fixes - string based paths -> boost::filesystem::path paths (I hope it's final) - New user data path on windows - New moving dir method on windows. --- Global.h | 13 +- client/CPlayerInterface.cpp | 10 +- editor/Editor.cpp | 2 +- launcher/StdInc.h | 11 +- launcher/launcherdirs.cpp | 4 +- launcher/mainwindow_moc.cpp | 8 +- launcher/modManager/cmodmanager.cpp | 2 +- launcher/settingsView/csettingsview_moc.cpp | 6 +- lib/CGameInterface.cpp | 32 ++-- lib/VCMIDirs.cpp | 171 ++++++++++++++++++-- lib/VCMIDirs.h | 9 -- lib/filesystem/CArchiveLoader.cpp | 29 ++-- lib/filesystem/CArchiveLoader.h | 4 +- lib/filesystem/CFileInfo.cpp | 29 ++-- lib/filesystem/CFileInputStream.cpp | 10 +- lib/filesystem/CFileInputStream.h | 6 +- lib/filesystem/CFilesystemLoader.cpp | 84 ++++++---- lib/filesystem/CFilesystemLoader.h | 10 +- lib/filesystem/Filesystem.cpp | 12 +- lib/logging/CBasicLogConfigurator.cpp | 5 +- lib/logging/CBasicLogConfigurator.h | 3 +- lib/logging/CLogger.cpp | 47 +++--- lib/logging/CLogger.h | 29 ++-- scripting/erm/ERMInterpreter.cpp | 12 +- test/CVcmiTestConfig.cpp | 2 +- 25 files changed, 359 insertions(+), 191 deletions(-) diff --git a/Global.h b/Global.h index e7e5729c4..6c0a01cc1 100644 --- a/Global.h +++ b/Global.h @@ -96,7 +96,11 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); /* ---------------------------------------------------------------------------- */ /* Commonly used C++, Boost headers */ /* ---------------------------------------------------------------------------- */ +#ifndef VCMI_WINDOWS #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define NOMINMAX // Exclude min/max macros from +#endif + #define _USE_MATH_DEFINES #include @@ -148,6 +152,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); #include #include #include +#include #include #include #include @@ -158,10 +163,6 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); #include #include -#ifdef VCMI_ANDROID - #include -#endif - #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -196,7 +197,7 @@ typedef boost::lock_guard TLockGuardRec; /* Macros */ /* ---------------------------------------------------------------------------- */ // Import + Export macro declarations -#ifdef _WIN32 +#ifdef VCMI_WINDOWS # ifdef __GNUC__ # define DLL_EXPORT __attribute__((dllexport)) # else @@ -210,7 +211,7 @@ typedef boost::lock_guard TLockGuardRec; # endif #endif -#ifdef _WIN32 +#ifdef VCMI_WINDOWS # ifdef __GNUC__ # define DLL_IMPORT __attribute__((dllimport)) # else diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index d243f2462..47ff04886 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1601,22 +1601,20 @@ int CPlayerInterface::getLastIndex( std::string namePrefix) path gamesDir = VCMIDirs::get().userSavePath(); std::map dates; //save number => datestamp - directory_iterator enddir; + const directory_iterator enddir; if(!exists(gamesDir)) create_directory(gamesDir); - - for (directory_iterator dir(gamesDir); dir!=enddir; dir++) + else + for (directory_iterator dir(gamesDir); dir != enddir; ++dir) { if(is_regular(dir->status())) { - std::string name = dir->path().leaf().string(); + std::string name = dir->path().filename().string(); if(starts_with(name, namePrefix) && ends_with(name, ".vcgm1")) { char nr = name[namePrefix.size()]; if(std::isdigit(nr)) - { dates[last_write_time(dir->path())] = boost::lexical_cast(nr); - } } } } diff --git a/editor/Editor.cpp b/editor/Editor.cpp index 822ac1d89..158662630 100644 --- a/editor/Editor.cpp +++ b/editor/Editor.cpp @@ -13,7 +13,7 @@ Editor::Editor(QWidget *parent) { // Setup default logging(enough for now) console = new CConsoleHandler; - CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Editor_log.txt", console); + CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Editor_log.txt", console); logConfig.configureDefault(); preinitDLL(console); diff --git a/launcher/StdInc.h b/launcher/StdInc.h index 751c21f85..aa3a6b8fe 100644 --- a/launcher/StdInc.h +++ b/launcher/StdInc.h @@ -8,4 +8,13 @@ #include #include #include -#include \ No newline at end of file +#include + +inline QString pathToQString(const boost::filesystem::path & path) +{ +#ifdef VCMI_WINDOWS + return QString::fromStdWString(path.wstring()); +#else + return QString::fromStdString(path.string()); +#endif +} \ No newline at end of file diff --git a/launcher/launcherdirs.cpp b/launcher/launcherdirs.cpp index 6e28e273a..d7b3b453d 100644 --- a/launcher/launcherdirs.cpp +++ b/launcher/launcherdirs.cpp @@ -18,10 +18,10 @@ CLauncherDirs & CLauncherDirs::get() QString CLauncherDirs::downloadsPath() { - return QString::fromUtf8(VCMIDirs::get().userCachePath().c_str()) + "/downloads"; + return pathToQString(VCMIDirs::get().userCachePath() / "downloads"); } QString CLauncherDirs::modsPath() { - return QString::fromUtf8(VCMIDirs::get().userDataPath().c_str()) + "/Mods"; + return pathToQString(VCMIDirs::get().userDataPath() / "Mods"); } diff --git a/launcher/mainwindow_moc.cpp b/launcher/mainwindow_moc.cpp index f7cee2d17..3536e925a 100644 --- a/launcher/mainwindow_moc.cpp +++ b/launcher/mainwindow_moc.cpp @@ -13,15 +13,15 @@ void MainWindow::load() { console = new CConsoleHandler; - CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Launcher_log.txt", console); + CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Launcher_log.txt", console); logConfig.configureDefault(); CResourceHandler::initialize(); CResourceHandler::load("config/filesystem.json"); for (auto & string : VCMIDirs::get().dataPaths()) - QDir::addSearchPath("icons", QString::fromUtf8(string.c_str()) + "/launcher/icons"); - QDir::addSearchPath("icons", QString::fromUtf8(VCMIDirs::get().userDataPath().c_str()) + "/launcher/icons"); + QDir::addSearchPath("icons", pathToQString(string / "launcher" / "icons")); + QDir::addSearchPath("icons", pathToQString(VCMIDirs::get().userDataPath() / "launcher" / "icons")); settings.init(); } @@ -46,7 +46,7 @@ MainWindow::~MainWindow() void MainWindow::on_startGameButon_clicked() { - startExecutable(QString::fromUtf8(VCMIDirs::get().clientPath().c_str())); + startExecutable(pathToQString(VCMIDirs::get().clientPath())); } void MainWindow::startExecutable(QString name) diff --git a/launcher/modManager/cmodmanager.cpp b/launcher/modManager/cmodmanager.cpp index 8fff2af44..40f00dcb4 100644 --- a/launcher/modManager/cmodmanager.cpp +++ b/launcher/modManager/cmodmanager.cpp @@ -39,7 +39,7 @@ CModManager::CModManager(CModList * modList): QString CModManager::settingsPath() { - return QString::fromUtf8(VCMIDirs::get().userConfigPath().c_str()) + "/modSettings.json"; + return pathToQString(VCMIDirs::get().userConfigPath() / "modSettings.json"); } void CModManager::loadModSettings() diff --git a/launcher/settingsView/csettingsview_moc.cpp b/launcher/settingsView/csettingsview_moc.cpp index 9013d836e..9f19a70bf 100644 --- a/launcher/settingsView/csettingsview_moc.cpp +++ b/launcher/settingsView/csettingsview_moc.cpp @@ -46,9 +46,9 @@ void CSettingsView::loadSettings() for (auto entry : urls.Vector()) ui->plainTextEditRepos->appendPlainText(QString::fromUtf8(entry.String().c_str())); - ui->lineEditUserDataDir->setText(QString::fromUtf8(VCMIDirs::get().userDataPath().c_str())); - ui->lineEditGameDir->setText(QString::fromUtf8(M_DATA_DIR)); - ui->lineEditTempDir->setText(QString::fromUtf8(VCMIDirs::get().userCachePath().c_str())); + ui->lineEditUserDataDir->setText(pathToQString(VCMIDirs::get().userDataPath())); + ui->lineEditGameDir->setText(pathToQString(VCMIDirs::get().binaryPath())); + ui->lineEditTempDir->setText(pathToQString(VCMIDirs::get().userCachePath())); std::string encoding = settings["general"]["encoding"].String(); size_t encodingIndex = boost::range::find(knownEncodingsList, encoding) - knownEncodingsList; diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index 88d9fed6d..21336bafa 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -35,7 +35,7 @@ extern "C" DLL_EXPORT void BattleAI_GetNewBattleAI(shared_ptr -shared_ptr createAny(std::string dllname, std::string methodName) +shared_ptr createAny(const boost::filesystem::path& libpath, const std::string& methodName) { typedef void(*TGetAIFun)(shared_ptr&); typedef void(*TGetNameFun)(char*); @@ -45,7 +45,13 @@ shared_ptr createAny(std::string dllname, std::string methodName) TGetAIFun getAI = nullptr; TGetNameFun getName = nullptr; -#ifdef __ANDROID__ +#ifndef VCMI_WINDOWS + std::string dllname = libpath.string(); + // I don't know other platforms. + // Somebody should remove it soon. +#endif + +#ifdef VCMI_ANDROID // this is awful but it seems using shared libraries on some devices is even worse if (dllname.find("libVCAI.so") != std::string::npos) { getName = (TGetNameFun)VCAI_GetAiName; @@ -61,12 +67,12 @@ shared_ptr createAny(std::string dllname, std::string methodName) } #else -#ifdef _WIN32 - HINSTANCE dll = LoadLibraryA(dllname.c_str()); +#ifdef VCMI_WINDOWS + HMODULE dll = LoadLibraryW(libpath.c_str()); if (dll) { - getName = (TGetNameFun)GetProcAddress(dll,"GetAiName"); - getAI = (TGetAIFun)GetProcAddress(dll,methodName.c_str()); + getName = (TGetNameFun)GetProcAddress(dll, "GetAiName"); + getAI = (TGetAIFun)GetProcAddress(dll, methodName.c_str()); } #else void *dll = dlopen(dllname.c_str(), RTLD_LOCAL | RTLD_LAZY); @@ -80,21 +86,20 @@ shared_ptr createAny(std::string dllname, std::string methodName) #endif if (!dll) { - logGlobal->errorStream() << "Cannot open dynamic library ("<errorStream() << "Cannot open dynamic library ("<errorStream() << dllname << " does not export method " << methodName; -#ifdef _WIN32 + logGlobal->errorStream() << libpath << " does not export method " << methodName; +#ifdef VCMI_WINDOWS FreeLibrary(dll); #else dlclose(dll); #endif throw std::runtime_error("Cannot find method " + methodName); } - -#endif // __ANDROID__ +#endif // VCMI_ANDROID getName(temp); logGlobal->infoStream() << "Loaded " << temp; @@ -113,9 +118,8 @@ shared_ptr createAnyAI(std::string dllname, std::string methodName) logGlobal->infoStream() << "Opening " << dllname; const boost::filesystem::path filePath = VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(dllname); - // TODO: createAny Should take boost::filesystem::path in argument. - auto ret = createAny(filePath.string(), methodName); - ret->dllName = dllname; + auto ret = createAny(filePath, methodName); + ret->dllName = std::move(dllname); return ret; } diff --git a/lib/VCMIDirs.cpp b/lib/VCMIDirs.cpp index f6135ae32..e397e0e2d 100644 --- a/lib/VCMIDirs.cpp +++ b/lib/VCMIDirs.cpp @@ -30,6 +30,75 @@ void IVCMIDirs::init() #include #include +// Generates script file named _temp.bat in 'to' directory and runs it +// Script will: +// - Wait util 'exeName' ends. +// - Copy all files from 'from' to 'to' +// - Ask user to replace files existed in 'to'. +// - Run 'exeName' +// - Delete itself. +bool StartBatchCopyDataProgram( + const bfs::path& from, const bfs::path& to, const bfs::path& exeName, + const bfs::path& currentPath = bfs::current_path()) +{ + static const char base[] = + "@echo off" "\n" + "echo Preparing to move VCMI data system." "\n" + + ":CLIENT_RUNNING_LOOP" "\n" + "TASKLIST | FIND /I %1% > nul" "\n" + "IF ERRORLEVEL 1 (" "\n" + "GOTO CLIENT_NOT_RUNNING" "\n" + ") ELSE (" "\n" + "echo %1% is still running..." "\n" + "echo Waiting until process ends..." "\n" + "ping 1.1.1.1 -n 1 -w 3000 > nul" "\n" // Sleep ~3 seconds. I love Windows :) + "goto :CLIENT_RUNNING_LOOP" "\n" + ")" "\n" + + ":CLIENT_NOT_RUNNING" "\n" + "echo %1% turned off..." "\n" + "echo Attempt to move datas." "\n" + "echo From: %2%" "\n" + "echo To: %4%" "\n" + "echo Please resolve any conflicts..." "\n" + "move /-Y %3% %4%" "\n" // Move all files from %3% to %4%. + // /-Y ask what to do when file exists in %4% + ":REMOVE_OLD_DIR" "\n" + "rd %2% || rem" "\n" // Remove empty directory. Sets error flag if fail. + "IF ERRORLEVEL 145 (" "\n" // Directory not empty + "echo Directory %2% is not empty." "\n" + "echo Please move rest of files manually now." "\n" + "pause" "\n" // Press any key to continue... + "goto REMOVE_OLD_DIR" "\n" + ")" "\n" + "echo Game data updated succefully." "\n" + "echo Please update your shortcuts." "\n" + "echo Press any key to start a game . . ." "\n" + "pause > nul" "\n" + "%5%" "\n" + "del \"%%~f0\"&exit" "\n" // Script deletes itself + ; + + const auto startGameString = + bfs::equivalent(currentPath, from) ? + (boost::format("start \"\" %1%") % (to / exeName)) : // Start game in new path. + (boost::format("start \"\" /D %1% %2%") % currentPath % (to / exeName)); // Start game in 'currentPath" + + const bfs::path bathFilename = to / "_temp.bat"; + bfs::ofstream bathFile(bathFilename, bfs::ofstream::trunc | bfs::ofstream::out); + if (!bathFile.is_open()) + return false; + bathFile << (boost::format(base) % exeName % from % (from / "*.*") % to % startGameString.str()).str(); + bathFile.close(); + + std::system(("start \"Updating VCMI datas\" /D \"" + to.string() + "\" \"" + bathFilename.string() + '\"').c_str()); + // start won't block std::system + // /D start bat in other directory insteand of current directory. + + return true; +} + class VCMIDirsWIN32 : public IVCMIDirs { public: @@ -50,6 +119,9 @@ class VCMIDirsWIN32 : public IVCMIDirs std::string genHelpString() const override; void init() override; + protected: + boost::filesystem::path oldUserDataPath() const; + boost::filesystem::path oldUserSavePath() const; }; void VCMIDirsWIN32::init() @@ -57,15 +129,21 @@ void VCMIDirsWIN32::init() // Call base (init dirs) IVCMIDirs::init(); - auto moveDirIfExists = [](const bfs::path& from, const bfs::path& to) + // Moves one directory (from) contents to another directory (to) + // Shows user the "moving file dialog" and ask to resolve conflits. + // If necessary updates current directory. + auto moveDirIfExists = [](const bfs::path& from, const bfs::path& to) -> bool { if (!bfs::is_directory(from)) - return; // Nothing to do here. Flies away. + return true; // Nothing to do here. Flies away. if (bfs::is_empty(from)) { + if (bfs::current_path() == from) + bfs::current_path(to); + bfs::remove(from); - return; // Nothing to do here. Flies away. + return true; // Nothing to do here. Flies away. } if (!bfs::is_directory(to)) @@ -103,23 +181,87 @@ void VCMIDirsWIN32::init() fileOp.lpszProgressTitle = nullptr; const int errorCode = SHFileOperationW(&fileOp); - if (errorCode != 0); // TODO: Log error. User should try to move files. - else if (fileOp.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); + if (errorCode != 0) // TODO: Log error. User should try to move files. + return false; + else if (fileOp.fAnyOperationsAborted) // TODO: Log warn. User aborted operation. User should move files. + return false; + else if (!bfs::is_empty(from)) // TODO: Log warn. Some files not moved. User should try to move files. + return false; + + if (bfs::current_path() == from) + bfs::current_path(to); + + // TODO: Log fact that we moved files succefully. + bfs::remove(from); + return true; + }; + + // Retrieves the fully qualified path for the file that contains the specified module. + // The module must have been loaded by the current process. + // If this parameter is nullptr, retrieves the path of the executable file of the current process. + auto getModulePath = [](HMODULE hModule) -> bfs::path + { + wchar_t exePathW[MAX_PATH]; + DWORD nSize = GetModuleFileNameW(hModule, exePathW, MAX_PATH); + DWORD error = GetLastError(); + // WARN: Windows XP don't set ERROR_INSUFFICIENT_BUFFER error. + if (nSize != 0 && error != ERROR_INSUFFICIENT_BUFFER) + return bfs::path(std::wstring(exePathW, nSize)); + // TODO: Error handling + return bfs::path(); }; - moveDirIfExists(userDataPath() / "Games", userSavePath()); + // Moves one directory contents to another directory + // Shows user the "moving file dialog" and ask to resolve conflicts. + // It takes into account that 'from' path can contain current executable. + // If necessary closes program and starts update script. + auto advancedMoveDirIfExists = [getModulePath, moveDirIfExists](const bfs::path& from, const bfs::path& to) -> bool + { + const bfs::path executablePath = getModulePath(nullptr); + + // VCMI cann't determine executable path. + // Use standard way to move directory and exit function. + if (executablePath.empty()) + return moveDirIfExists(from, to); + + const bfs::path executableName = executablePath.filename(); + + // Current executabl isn't in 'from' path. + // Use standard way to move directory and exit function. + if (!bfs::equivalent(executablePath, from / executableName)) + return moveDirIfExists(from, to); + + // Try standard way to move directory. + // I don't know how other systems, but Windows 8.1 allow to move running executable. + if (moveDirIfExists(from, to)) + return true; + + // Start copying script and exit program. + if (StartBatchCopyDataProgram(from, to, executableName)) + exit(ERROR_SUCCESS); + + // Everything failed :C + return false; + }; + + moveDirIfExists(oldUserSavePath(), userSavePath()); + advancedMoveDirIfExists(oldUserDataPath(), userDataPath()); } bfs::path VCMIDirsWIN32::userDataPath() const { wchar_t profileDir[MAX_PATH]; - // The user's profile folder. A typical path is C:\Users\username. - // FIXME: Applications should not create files or folders at this level; - // they should put their data under the locations referred to by CSIDL_APPDATA or CSIDL_LOCAL_APPDATA. + if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_MYDOCUMENTS, FALSE) != FALSE) + return bfs::path(profileDir) / "My Games\\vcmi"; + + return "."; +} + +bfs::path VCMIDirsWIN32::oldUserDataPath() const +{ + wchar_t profileDir[MAX_PATH]; + if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_PROFILE, FALSE) == FALSE) // WinAPI way failed { #if defined(_MSC_VER) && _MSC_VER >= 1700 @@ -145,6 +287,8 @@ bfs::path VCMIDirsWIN32::userDataPath() const //return dataPaths()[0] ???; } +bfs::path VCMIDirsWIN32::oldUserSavePath() const { return userDataPath() / "Games"; } + bfs::path VCMIDirsWIN32::userCachePath() const { return userDataPath(); } bfs::path VCMIDirsWIN32::userConfigPath() const { return userDataPath() / "config"; } @@ -421,6 +565,9 @@ namespace VCMIDirs static bool initialized = false; if (!initialized) { + std::locale::global(boost::locale::generator().generate("en_US.UTF-8")); + boost::filesystem::path::imbue(std::locale()); + singleton.init(); initialized = true; } diff --git a/lib/VCMIDirs.h b/lib/VCMIDirs.h index 37e3150af..fa1e43221 100644 --- a/lib/VCMIDirs.h +++ b/lib/VCMIDirs.h @@ -9,15 +9,6 @@ */ #pragma once -// STL C++ -#include -#include - -// Boost -#include - -// TODO: File should be renamed to IVCMIDirs.h - class DLL_LINKAGE IVCMIDirs { public: diff --git a/lib/filesystem/CArchiveLoader.cpp b/lib/filesystem/CArchiveLoader.cpp index e1ae5427b..fdebd315a 100644 --- a/lib/filesystem/CArchiveLoader.cpp +++ b/lib/filesystem/CArchiveLoader.cpp @@ -13,9 +13,9 @@ ArchiveEntry::ArchiveEntry() } -CArchiveLoader::CArchiveLoader(const std::string &mountPoint, const std::string & archive): - archive(archive), - mountPoint(mountPoint) +CArchiveLoader::CArchiveLoader(std::string _mountPoint, boost::filesystem::path _archive) : + archive(std::move(_archive)), + mountPoint(std::move(_mountPoint)) { // Open archive file(.snd, .vid, .lod) CFileInputStream fileStream(archive); @@ -25,28 +25,19 @@ CArchiveLoader::CArchiveLoader(const std::string &mountPoint, const std::string return; // Retrieve file extension of archive in uppercase - CFileInfo fileInfo(archive); - std::string ext = fileInfo.getExtension(); - boost::to_upper(ext); + const std::string ext = boost::to_upper_copy(archive.extension().string()); // Init the specific lod container format if(ext == ".LOD" || ext == ".PAC") - { initLODArchive(mountPoint, fileStream); - } else if(ext == ".VID") - { initVIDArchive(mountPoint, fileStream); - } else if(ext == ".SND") - { initSNDArchive(mountPoint, fileStream); - } else - { - throw std::runtime_error("LOD archive format unknown. Cannot deal with " + archive); - } - logGlobal->traceStream() << ext << "Archive loaded, " << entries.size() << " files found"; + throw std::runtime_error("LOD archive format unknown. Cannot deal with " + archive.string()); + + logGlobal->traceStream() << ext << "Archive \""<(filename), 16); @@ -90,7 +81,7 @@ void CArchiveLoader::initVIDArchive(const std::string &mountPoint, CFileInputStr std::set offsets; // Insert entries to list - for(ui32 i = 0; i < totalFiles; i++) + for(ui32 i = 0; i < totalFiles; ++i) { char filename[40]; reader.read(reinterpret_cast(filename), 40); @@ -122,7 +113,7 @@ void CArchiveLoader::initSNDArchive(const std::string &mountPoint, CFileInputStr ui32 totalFiles = reader.readUInt32(); // Insert entries to list - for(ui32 i = 0; i < totalFiles; i++) + for(ui32 i = 0; i < totalFiles; ++i) { char filename[40]; reader.read(reinterpret_cast(filename), 40); diff --git a/lib/filesystem/CArchiveLoader.h b/lib/filesystem/CArchiveLoader.h index e4300a855..7d9b5da78 100644 --- a/lib/filesystem/CArchiveLoader.h +++ b/lib/filesystem/CArchiveLoader.h @@ -55,7 +55,7 @@ public: * * @throws std::runtime_error if the archive wasn't found or if the archive isn't supported */ - explicit CArchiveLoader(const std::string & mountPoint, const std::string & archive); + CArchiveLoader(std::string mountPoint, boost::filesystem::path archive); /// Interface implementation /// @see ISimpleResourceLoader @@ -87,7 +87,7 @@ private: void initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream); /** The file path to the archive which is scanned and indexed. */ - std::string archive; + boost::filesystem::path archive; std::string mountPoint; diff --git a/lib/filesystem/CFileInfo.cpp b/lib/filesystem/CFileInfo.cpp index 59aef320d..c2347e897 100644 --- a/lib/filesystem/CFileInfo.cpp +++ b/lib/filesystem/CFileInfo.cpp @@ -41,17 +41,17 @@ std::string CFileInfo::getPath() const std::string CFileInfo::getExtension() const { // Get position of file extension dot - size_t dotPos = name.find_last_of("/."); + size_t dotPos = name.find_last_of('.'); - if(dotPos != std::string::npos && name[dotPos] == '.') + if(dotPos != std::string::npos) return name.substr(dotPos); - else - return ""; + + return ""; } std::string CFileInfo::getFilename() const { - size_t found = name.find_last_of("/\\"); + const size_t found = name.find_last_of("/\\"); return name.substr(found + 1); } @@ -60,9 +60,9 @@ std::string CFileInfo::getStem() const std::string rslt = name; // Remove file extension - size_t dotPos = name.find_last_of("/."); + const size_t dotPos = name.find_last_of('.'); - if(dotPos != std::string::npos && name[dotPos] == '.') + if(dotPos != std::string::npos) rslt.erase(dotPos); return rslt; @@ -70,18 +70,19 @@ std::string CFileInfo::getStem() const std::string CFileInfo::getBaseName() const { - size_t begin = name.find_last_of("/"); - size_t end = name.find_last_of("/."); - - if(end != std::string::npos && name[end] == '/') - end = std::string::npos; + size_t begin = name.find_last_of("/\\"); + size_t end = name.find_last_of("."); if(begin == std::string::npos) begin = 0; else - begin++; + ++begin; + + if (end < begin) + end = std::string::npos; - return name.substr(begin, end - begin); + size_t len = (end == std::string::npos ? std::string::npos : end - begin); + return name.substr(begin, len); } EResType::Type CFileInfo::getType() const diff --git a/lib/filesystem/CFileInputStream.cpp b/lib/filesystem/CFileInputStream.cpp index 66b70e11d..758948325 100644 --- a/lib/filesystem/CFileInputStream.cpp +++ b/lib/filesystem/CFileInputStream.cpp @@ -3,7 +3,7 @@ #include "CFileInfo.h" -CFileInputStream::CFileInputStream(const std::string & file, si64 start, si64 size) +CFileInputStream::CFileInputStream(const boost::filesystem::path & file, si64 start, si64 size) { open(file, start, size); } @@ -18,14 +18,12 @@ CFileInputStream::~CFileInputStream() fileStream.close(); } -void CFileInputStream::open(const std::string & file, si64 start, si64 size) +void CFileInputStream::open(const boost::filesystem::path & file, si64 start, si64 size) { - fileStream.open(file.c_str(), std::ios::in | std::ios::binary); + fileStream.open(file, std::ios::in | std::ios::binary); if (fileStream.fail()) - { - throw std::runtime_error("File " + file + " isn't available."); - } + throw std::runtime_error("File " + file.string() + " isn't available."); dataStart = start; dataSize = size; diff --git a/lib/filesystem/CFileInputStream.h b/lib/filesystem/CFileInputStream.h index a6d1d74cf..31e57f069 100644 --- a/lib/filesystem/CFileInputStream.h +++ b/lib/filesystem/CFileInputStream.h @@ -25,7 +25,7 @@ public: * * @see CFileInputStream::open */ - CFileInputStream(const std::string & file, si64 start=0, si64 size=0); + CFileInputStream(const boost::filesystem::path & file, si64 start = 0, si64 size = 0); /** * C-tor. Opens the specified file. @@ -88,11 +88,11 @@ private: * * @throws std::runtime_error if file wasn't found */ - void open(const std::string & file, si64 start, si64 size); + void open(const boost::filesystem::path & file, si64 start, si64 size); si64 dataStart; si64 dataSize; /** Native c++ input file stream object. */ - std::ifstream fileStream; + boost::filesystem::ifstream fileStream; }; diff --git a/lib/filesystem/CFilesystemLoader.cpp b/lib/filesystem/CFilesystemLoader.cpp index a35c7d892..0704c8a06 100644 --- a/lib/filesystem/CFilesystemLoader.cpp +++ b/lib/filesystem/CFilesystemLoader.cpp @@ -4,9 +4,11 @@ #include "CFileInfo.h" #include "CFileInputStream.h" -CFilesystemLoader::CFilesystemLoader(const std::string &mountPoint, const std::string & baseDirectory, size_t depth, bool initial): - baseDirectory(baseDirectory), - mountPoint(mountPoint), +namespace bfs = boost::filesystem; + +CFilesystemLoader::CFilesystemLoader(std::string _mountPoint, bfs::path baseDirectory, size_t depth, bool initial): + baseDirectory(std::move(baseDirectory)), + mountPoint(std::move(_mountPoint)), fileList(listFiles(mountPoint, depth, initial)) { logGlobal->traceStream() << "Filesystem loaded, " << fileList.size() << " files found"; @@ -16,7 +18,7 @@ std::unique_ptr CFilesystemLoader::load(const ResourceID & resourc { assert(fileList.count(resourceName)); - std::unique_ptr stream(new CFileInputStream(baseDirectory + '/' + fileList.at(resourceName))); + std::unique_ptr stream(new CFileInputStream(baseDirectory / fileList.at(resourceName))); return stream; } @@ -34,7 +36,7 @@ boost::optional CFilesystemLoader::getResourceName(const ResourceID { assert(existsResource(resourceName)); - return baseDirectory + '/' + fileList.at(resourceName); + return (baseDirectory / fileList.at(resourceName)).string(); } std::unordered_set CFilesystemLoader::getFilteredFiles(std::function filter) const @@ -45,7 +47,8 @@ std::unordered_set CFilesystemLoader::getFilteredFiles(std::function { if (filter(file.first)) foundID.insert(file.first); - } return foundID; + } + return foundID; } bool CFilesystemLoader::createResource(std::string filename, bool update) @@ -65,7 +68,7 @@ bool CFilesystemLoader::createResource(std::string filename, bool update) if (!update) { - std::ofstream newfile (baseDirectory + "/" + filename); + bfs::ofstream newfile(baseDirectory / filename); if (!newfile.good()) return false; } @@ -73,49 +76,72 @@ bool CFilesystemLoader::createResource(std::string filename, bool update) return true; } -std::unordered_map CFilesystemLoader::listFiles(const std::string &mountPoint, size_t depth, bool initial) const +std::unordered_map CFilesystemLoader::listFiles(const std::string &mountPoint, size_t depth, bool initial) const { - std::set initialTypes; - initialTypes.insert(EResType::DIRECTORY); - initialTypes.insert(EResType::TEXT); - initialTypes.insert(EResType::ARCHIVE_LOD); - initialTypes.insert(EResType::ARCHIVE_VID); - initialTypes.insert(EResType::ARCHIVE_SND); - initialTypes.insert(EResType::ARCHIVE_ZIP); + static const EResType::Type initArray[] = { + EResType::DIRECTORY, + EResType::TEXT, + EResType::ARCHIVE_LOD, + EResType::ARCHIVE_VID, + EResType::ARCHIVE_SND, + EResType::ARCHIVE_ZIP }; + static const std::set initialTypes(initArray, initArray + ARRAY_COUNT(initArray)); - assert(boost::filesystem::is_directory(baseDirectory)); - std::unordered_map fileList; + assert(bfs::is_directory(baseDirectory)); + std::unordered_map fileList; - std::vector path;//vector holding relative path to our file + std::vector path; //vector holding relative path to our file - boost::filesystem::recursive_directory_iterator enddir; - boost::filesystem::recursive_directory_iterator it(baseDirectory, boost::filesystem::symlink_option::recurse); + bfs::recursive_directory_iterator enddir; + bfs::recursive_directory_iterator it(baseDirectory, bfs::symlink_option::recurse); for(; it != enddir; ++it) { EResType::Type type; - if (boost::filesystem::is_directory(it->status())) + if (bfs::is_directory(it->status())) { - path.resize(it.level()+1); - path.back() = it->path().leaf().string(); + path.resize(it.level() + 1); + path.back() = it->path().filename(); // don't iterate into directory if depth limit reached it.no_push(depth <= it.level()); type = EResType::DIRECTORY; } else - type = EResTypeHelper::getTypeFromExtension(boost::filesystem::extension(*it)); + type = EResTypeHelper::getTypeFromExtension(it->path().extension().string()); if (!initial || vstd::contains(initialTypes, type)) { //reconstruct relative filename (not possible via boost AFAIK) - std::string filename; - for (size_t i=0; ipath().leaf().string(); + bfs::path filename; + const size_t iterations = std::min((size_t)it.level(), path.size()); + if (iterations) + { + filename = path.front(); + for (size_t i = 1; i < iterations; ++i) + filename /= path[i]; + filename /= it->path().filename(); + } + else + filename = it->path().filename(); - fileList[ResourceID(mountPoint + filename, type)] = filename; + std::string resName; + if (bfs::path::preferred_separator != '/') + { + // resource names are using UNIX slashes (/) + resName.reserve(resName.size() + filename.native().size()); + resName = mountPoint; + for (const char c : filename.string()) + if (c != bfs::path::preferred_separator) + resName.push_back(c); + else + resName.push_back('/'); + } + else + resName = mountPoint + filename.string(); + + fileList[ResourceID(resName, type)] = std::move(filename); } } diff --git a/lib/filesystem/CFilesystemLoader.h b/lib/filesystem/CFilesystemLoader.h index 03a597497..e7cfd9a81 100644 --- a/lib/filesystem/CFilesystemLoader.h +++ b/lib/filesystem/CFilesystemLoader.h @@ -30,7 +30,7 @@ public: * * @throws std::runtime_error if the base directory is not a directory or if it is not available */ - explicit CFilesystemLoader(const std::string & mountPoint, const std::string & baseDirectory, size_t depth = 16, bool initial = false); + explicit CFilesystemLoader(std::string mountPoint, boost::filesystem::path baseDirectory, size_t depth = 16, bool initial = false); /// Interface implementation /// @see ISimpleResourceLoader @@ -39,11 +39,11 @@ public: std::string getMountPoint() const override; bool createResource(std::string filename, bool update = false) override; boost::optional getResourceName(const ResourceID & resourceName) const override; - std::unordered_set getFilteredFiles(std::function filter) const; + std::unordered_set getFilteredFiles(std::function filter) const override; private: /** The base directory which is scanned and indexed. */ - std::string baseDirectory; + boost::filesystem::path baseDirectory; std::string mountPoint; @@ -51,7 +51,7 @@ private: * key = ResourceID for resource loader * value = name that can be used to access file */ - std::unordered_map fileList; + std::unordered_map fileList; /** * Returns a list of pathnames denoting the files in the directory denoted by this pathname. @@ -62,5 +62,5 @@ private: * @return a list of pathnames denoting the files and directories in the directory denoted by this pathname * The array will be empty if the directory is empty. Ptr is null if the directory doesn't exist or if it isn't a directory. */ - std::unordered_map listFiles(const std::string &mountPoint, size_t depth, bool initial) const; + std::unordered_map listFiles(const std::string &mountPoint, size_t depth, bool initial) const; }; diff --git a/lib/filesystem/Filesystem.cpp b/lib/filesystem/Filesystem.cpp index b34954ec4..c9c9790e5 100644 --- a/lib/filesystem/Filesystem.cpp +++ b/lib/filesystem/Filesystem.cpp @@ -130,19 +130,18 @@ ISimpleResourceLoader * CResourceHandler::createInitial() auto filename = loader->getResourceName(ID); if (filename) { - auto dir = new CFilesystemLoader(URI + "/", *filename, depth, true); + auto dir = new CFilesystemLoader(URI + '/', *filename, depth, true); initialLoader->addLoader(dir, false); } } }; - // 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.string(), 0, true), false); + initialLoader->addLoader(new CFilesystemLoader("", path, 0, true), false); } - initialLoader->addLoader(new CFilesystemLoader("", VCMIDirs::get().userDataPath().string(), 0, true), false); + initialLoader->addLoader(new CFilesystemLoader("", VCMIDirs::get().userDataPath(), 0, true), false); recurseInDir("CONFIG", 0);// look for configs recurseInDir("DATA", 0); // look for archives @@ -167,10 +166,9 @@ void CResourceHandler::initialize() // |-saves // |-config - // TODO: CFilesystemLoader should take boost::filesystem::path knownLoaders["root"] = new CFilesystemList(); - knownLoaders["saves"] = new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath().string()); - knownLoaders["config"] = new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath().string()); + knownLoaders["saves"] = new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath()); + knownLoaders["config"] = new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath()); auto localFS = new CFilesystemList(); localFS->addLoader(knownLoaders["saves"], true); diff --git a/lib/logging/CBasicLogConfigurator.cpp b/lib/logging/CBasicLogConfigurator.cpp index 2d95e3ba7..97d0d3d35 100644 --- a/lib/logging/CBasicLogConfigurator.cpp +++ b/lib/logging/CBasicLogConfigurator.cpp @@ -3,10 +3,7 @@ #include "../CConfigHandler.h" -CBasicLogConfigurator::CBasicLogConfigurator(const boost::filesystem::path & filePath, CConsoleHandler * const console) : - filePath(filePath), console(console), appendToLogFile(false) {} - -CBasicLogConfigurator::CBasicLogConfigurator(boost::filesystem::path && filePath, CConsoleHandler * const console) : +CBasicLogConfigurator::CBasicLogConfigurator(boost::filesystem::path filePath, CConsoleHandler * const console) : filePath(std::move(filePath)), console(console), appendToLogFile(false) {} void CBasicLogConfigurator::configureDefault() diff --git a/lib/logging/CBasicLogConfigurator.h b/lib/logging/CBasicLogConfigurator.h index f9f322ca7..498f2a32c 100644 --- a/lib/logging/CBasicLogConfigurator.h +++ b/lib/logging/CBasicLogConfigurator.h @@ -22,8 +22,7 @@ class JsonNode; class DLL_LINKAGE CBasicLogConfigurator { public: - CBasicLogConfigurator(const boost::filesystem::path & filePath, CConsoleHandler * const console); - CBasicLogConfigurator(boost::filesystem::path && filePath, CConsoleHandler * const console); + CBasicLogConfigurator(boost::filesystem::path filePath, 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. diff --git a/lib/logging/CLogger.cpp b/lib/logging/CLogger.cpp index 62dfbdda9..63fa95b41 100644 --- a/lib/logging/CLogger.cpp +++ b/lib/logging/CLogger.cpp @@ -1,14 +1,30 @@ #include "StdInc.h" #include "CLogger.h" +#ifdef VCMI_ANDROID +#include + +namespace ELogLevel +{ + int toAndroid(ELogLevel logLevel) + { + switch (logLevel) + { + case TRACE: return ANDROID_LOG_VERBOSE; + case DEBUG: return ANDROID_LOG_DEBUG; + case INFO: return ANDROID_LOG_INFO; + case WARN: return ANDROID_LOG_WARN; + case ERROR: return ANDROID_LOG_ERROR; + default:; + } + return ANDROID_LOG_UNKNOWN; + } +} +#endif + const std::string CLoggerDomain::DOMAIN_GLOBAL = "global"; -CLoggerDomain::CLoggerDomain(const std::string & name) : name(name) -{ - if(name.empty()) - throw std::runtime_error("Logger domain cannot be empty."); -} -CLoggerDomain::CLoggerDomain(std::string && name) : name(std::move(name)) +CLoggerDomain::CLoggerDomain(std::string name) : name(std::move(name)) { if (this->name.empty()) throw std::runtime_error("Logger domain cannot be empty."); @@ -302,15 +318,16 @@ CLogConsoleTarget::CLogConsoleTarget(CConsoleHandler * console) : console(consol void CLogConsoleTarget::write(const LogRecord & record) { - if(threshold > record.level) return; + if(threshold > record.level) + return; std::string message = formatter.format(record); #ifdef VCMI_ANDROID - __android_log_print(ANDROID_LOG_INFO, "VCMI", "%s", message.c_str()); + __android_log_write(ELogLevel::toAndroid(record.level), "VCMI", message.c_str()); #endif - bool printToStdErr = record.level >= ELogLevel::WARN; + const bool printToStdErr = record.level >= ELogLevel::WARN; if(console) { const EConsoleTextColor::EConsoleTextColor textColor = @@ -340,22 +357,12 @@ void CLogConsoleTarget::setFormatter(const CLogFormatter & formatter) { this->fo const CColorMapping & CLogConsoleTarget::getColorMapping() const { return colorMapping; } void CLogConsoleTarget::setColorMapping(const CColorMapping & colorMapping) { this->colorMapping = colorMapping; } -CLogFileTarget::CLogFileTarget(const boost::filesystem::path & filePath, bool append /*= true*/) - : file(filePath, append ? std::ios_base::app : std::ios_base::out) -{ - formatter.setPattern("%d %l %n [%t] - %m"); -} -CLogFileTarget::CLogFileTarget(boost::filesystem::path && filePath, bool append /*= true*/) +CLogFileTarget::CLogFileTarget(boost::filesystem::path filePath, bool append /*= true*/) : file(std::move(filePath), append ? std::ios_base::app : std::ios_base::out) { formatter.setPattern("%d %l %n [%t] - %m"); } -CLogFileTarget::~CLogFileTarget() -{ - file.close(); -} - void CLogFileTarget::write(const LogRecord & record) { TLockGuard _(mx); diff --git a/lib/logging/CLogger.h b/lib/logging/CLogger.h index 77943e5b9..f72bca49e 100644 --- a/lib/logging/CLogger.h +++ b/lib/logging/CLogger.h @@ -19,15 +19,19 @@ class ILogTarget; namespace ELogLevel { -enum ELogLevel -{ - NOT_SET = 0, - TRACE, - DEBUG, - INFO, - WARN, - ERROR -}; + enum ELogLevel + { + NOT_SET = 0, + TRACE, + DEBUG, + INFO, + WARN, + ERROR + }; + + #ifdef VCMI_ANDROID + int toAndroid(ELogLevel logLevel); + #endif } /// The class CLoggerDomain provides convenient access to super domains from a sub domain. @@ -36,8 +40,7 @@ class DLL_LINKAGE CLoggerDomain public: /// Constructs a CLoggerDomain with the domain designated by name. /// Sub-domains can be specified by separating domains by a dot, e.g. "ai.battle". The global domain is named "global". - explicit CLoggerDomain(const std::string & name); - explicit CLoggerDomain(std::string && name); + explicit CLoggerDomain(std::string name); const std::string& getName() const; CLoggerDomain getParent() const; @@ -288,9 +291,7 @@ class DLL_LINKAGE CLogFileTarget : public ILogTarget public: /// Constructs a CLogFileTarget and opens the file designated by file_path. If the append parameter is true, the file /// will be appended to. Otherwise the file designated by filePath will be truncated before being opened. - explicit CLogFileTarget(const boost::filesystem::path & file_path, bool append = true); - explicit CLogFileTarget(boost::filesystem::path && file_path, bool append = true); - ~CLogFileTarget(); + explicit CLogFileTarget(boost::filesystem::path file_path, bool append = true); const CLogFormatter & getFormatter() const; void setFormatter(const CLogFormatter & formatter); diff --git a/scripting/erm/ERMInterpreter.cpp b/scripting/erm/ERMInterpreter.cpp index 9119ad153..7205327e4 100644 --- a/scripting/erm/ERMInterpreter.cpp +++ b/scripting/erm/ERMInterpreter.cpp @@ -368,19 +368,19 @@ void ERMInterpreter::scanForScripts() { using namespace boost::filesystem; //parser checking - if(!exists(VCMIDirs::get().dataPaths().back() + "/Data/s/")) + const path dataPath = VCMIDirs::get().dataPaths().back() / "Data" / "s"; + if(!exists(dataPath)) { - logGlobal->warnStream() << "Warning: Folder " << VCMIDirs::get().dataPaths().back() << "/Data/s/ doesn't exist!"; + logGlobal->warnStream() << "Warning: Folder " << dataPath << " doesn't exist!"; return; } directory_iterator enddir; - for (directory_iterator dir(VCMIDirs::get().dataPaths().back() + "/Data/s"); dir!=enddir; dir++) + for (directory_iterator dir(dataPath); dir!=enddir; dir++) { if(is_regular(dir->status())) { - std::string name = dir->path().leaf().string(); - if( boost::algorithm::ends_with(name, ".erm") || - boost::algorithm::ends_with(name, ".verm") ) + const std::string ext = boost::to_upper_copy(dir->path().extension().string()); + if (ext == ".ERM" || ext == ".VERM") { ERMParser ep(dir->path().string()); FileInfo * finfo = new FileInfo; diff --git a/test/CVcmiTestConfig.cpp b/test/CVcmiTestConfig.cpp index a56c7f380..cbe0deced 100644 --- a/test/CVcmiTestConfig.cpp +++ b/test/CVcmiTestConfig.cpp @@ -22,7 +22,7 @@ CVcmiTestConfig::CVcmiTestConfig() { console = new CConsoleHandler; - CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Test_log.txt", console); + CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Test_log.txt", console); logConfig.configureDefault(); preinitDLL(console); settings.init();