1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

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.
This commit is contained in:
Karol 2014-08-21 22:26:28 +02:00
parent 2da6d9e7dd
commit 958839668c
25 changed files with 359 additions and 191 deletions

View File

@ -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 <Windows.h>
#endif
#define _USE_MATH_DEFINES
#include <cstdio>
@ -148,6 +152,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
#include <boost/format.hpp>
#include <boost/functional/hash.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/locale/generator.hpp>
#include <boost/logic/tribool.hpp>
#include <boost/optional.hpp>
#include <boost/program_options.hpp>
@ -158,10 +163,6 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
#include <boost/variant.hpp>
#include <boost/math/special_functions/round.hpp>
#ifdef VCMI_ANDROID
#include <android/log.h>
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
@ -196,7 +197,7 @@ typedef boost::lock_guard<boost::recursive_mutex> 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<boost::recursive_mutex> TLockGuardRec;
# endif
#endif
#ifdef _WIN32
#ifdef VCMI_WINDOWS
# ifdef __GNUC__
# define DLL_IMPORT __attribute__((dllimport))
# else

View File

@ -1601,25 +1601,23 @@ int CPlayerInterface::getLastIndex( std::string namePrefix)
path gamesDir = VCMIDirs::get().userSavePath();
std::map<std::time_t, int> 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<int>(nr);
}
}
}
}
if(!dates.empty())
return (--dates.end())->second; //return latest file number

View File

@ -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);

View File

@ -9,3 +9,12 @@
#include <QList>
#include <QString>
#include <QFile>
inline QString pathToQString(const boost::filesystem::path & path)
{
#ifdef VCMI_WINDOWS
return QString::fromStdWString(path.wstring());
#else
return QString::fromStdString(path.string());
#endif
}

View File

@ -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");
}

View File

@ -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)

View File

@ -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()

View File

@ -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;

View File

@ -35,7 +35,7 @@ extern "C" DLL_EXPORT void BattleAI_GetNewBattleAI(shared_ptr<CBattleGameInterfa
#endif
template<typename rett>
shared_ptr<rett> createAny(std::string dllname, std::string methodName)
shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const std::string& methodName)
{
typedef void(*TGetAIFun)(shared_ptr<rett>&);
typedef void(*TGetNameFun)(char*);
@ -45,7 +45,13 @@ shared_ptr<rett> 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<rett> 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<rett> createAny(std::string dllname, std::string methodName)
#endif
if (!dll)
{
logGlobal->errorStream() << "Cannot open dynamic library ("<<dllname<<"). Throwing...";
logGlobal->errorStream() << "Cannot open dynamic library ("<<libpath<<"). Throwing...";
throw std::runtime_error("Cannot open dynamic library");
}
else if(!getName || !getAI)
{
logGlobal->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<rett> 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<rett>(filePath.string(), methodName);
ret->dllName = dllname;
auto ret = createAny<rett>(filePath, methodName);
ret->dllName = std::move(dllname);
return ret;
}

View File

@ -30,6 +30,75 @@ void IVCMIDirs::init()
#include <Shlobj.h>
#include <Shellapi.h>
// 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.
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;
};
moveDirIfExists(userDataPath() / "Games", userSavePath());
// 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();
};
// 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;
}

View File

@ -9,15 +9,6 @@
*/
#pragma once
// STL C++
#include <string>
#include <vector>
// Boost
#include <boost/filesystem/path.hpp>
// TODO: File should be renamed to IVCMIDirs.h
class DLL_LINKAGE IVCMIDirs
{
public:

View File

@ -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 \""<<archive.filename()<<"\" loaded (" << entries.size() << " files found).";
}
void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStream & fileStream)
@ -61,7 +52,7 @@ void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStr
fileStream.seek(0x5c);
// Insert entries to list
for(ui32 i = 0; i < totalFiles; i++)
for(ui32 i = 0; i < totalFiles; ++i)
{
char filename[16];
reader.read(reinterpret_cast<ui8*>(filename), 16);
@ -90,7 +81,7 @@ void CArchiveLoader::initVIDArchive(const std::string &mountPoint, CFileInputStr
std::set<int> 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<ui8*>(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<ui8*>(filename), 40);

View File

@ -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;

View File

@ -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 "";
}
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;
return name.substr(begin, end - begin);
if (end < begin)
end = std::string::npos;
size_t len = (end == std::string::npos ? std::string::npos : end - begin);
return name.substr(begin, len);
}
EResType::Type CFileInfo::getType() const

View File

@ -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;

View File

@ -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;
};

View File

@ -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<CInputStream> CFilesystemLoader::load(const ResourceID & resourc
{
assert(fileList.count(resourceName));
std::unique_ptr<CInputStream> stream(new CFileInputStream(baseDirectory + '/' + fileList.at(resourceName)));
std::unique_ptr<CInputStream> stream(new CFileInputStream(baseDirectory / fileList.at(resourceName)));
return stream;
}
@ -34,7 +36,7 @@ boost::optional<std::string> CFilesystemLoader::getResourceName(const ResourceID
{
assert(existsResource(resourceName));
return baseDirectory + '/' + fileList.at(resourceName);
return (baseDirectory / fileList.at(resourceName)).string();
}
std::unordered_set<ResourceID> CFilesystemLoader::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
@ -45,7 +47,8 @@ std::unordered_set<ResourceID> 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<ResourceID, std::string> CFilesystemLoader::listFiles(const std::string &mountPoint, size_t depth, bool initial) const
std::unordered_map<ResourceID, bfs::path> CFilesystemLoader::listFiles(const std::string &mountPoint, size_t depth, bool initial) const
{
std::set<EResType::Type> 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<EResType::Type> initialTypes(initArray, initArray + ARRAY_COUNT(initArray));
assert(boost::filesystem::is_directory(baseDirectory));
std::unordered_map<ResourceID, std::string> fileList;
assert(bfs::is_directory(baseDirectory));
std::unordered_map<ResourceID, bfs::path> fileList;
std::vector<std::string> path;//vector holding relative path to our file
std::vector<bfs::path> 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; i<it.level() && i<path.size(); i++)
filename += path[i] + '/';
filename += it->path().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);
}
}

View File

@ -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<std::string> getResourceName(const ResourceID & resourceName) const override;
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const;
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> 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<ResourceID, std::string> fileList;
std::unordered_map<ResourceID, boost::filesystem::path> 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<ResourceID, std::string> listFiles(const std::string &mountPoint, size_t depth, bool initial) const;
std::unordered_map<ResourceID, boost::filesystem::path> listFiles(const std::string &mountPoint, size_t depth, bool initial) const;
};

View File

@ -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);

View File

@ -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()

View File

@ -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.

View File

@ -1,14 +1,30 @@
#include "StdInc.h"
#include "CLogger.h"
#ifdef VCMI_ANDROID
#include <android/log.h>
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);

View File

@ -19,15 +19,19 @@ class ILogTarget;
namespace ELogLevel
{
enum ELogLevel
{
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);

View File

@ -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;

View File

@ -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();