mirror of
https://github.com/vcmi/vcmi.git
synced 2025-05-15 22:16:37 +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:
parent
2da6d9e7dd
commit
958839668c
13
Global.h
13
Global.h
@ -96,7 +96,11 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
|
|||||||
/* ---------------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------------- */
|
||||||
/* Commonly used C++, Boost headers */
|
/* Commonly used C++, Boost headers */
|
||||||
/* ---------------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------------- */
|
||||||
|
#ifndef VCMI_WINDOWS
|
||||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
#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
|
#define _USE_MATH_DEFINES
|
||||||
|
|
||||||
#include <cstdio>
|
#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/format.hpp>
|
||||||
#include <boost/functional/hash.hpp>
|
#include <boost/functional/hash.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/locale/generator.hpp>
|
||||||
#include <boost/logic/tribool.hpp>
|
#include <boost/logic/tribool.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <boost/program_options.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/variant.hpp>
|
||||||
#include <boost/math/special_functions/round.hpp>
|
#include <boost/math/special_functions/round.hpp>
|
||||||
|
|
||||||
#ifdef VCMI_ANDROID
|
|
||||||
#include <android/log.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
#endif
|
#endif
|
||||||
@ -196,7 +197,7 @@ typedef boost::lock_guard<boost::recursive_mutex> TLockGuardRec;
|
|||||||
/* Macros */
|
/* Macros */
|
||||||
/* ---------------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------------- */
|
||||||
// Import + Export macro declarations
|
// Import + Export macro declarations
|
||||||
#ifdef _WIN32
|
#ifdef VCMI_WINDOWS
|
||||||
# ifdef __GNUC__
|
# ifdef __GNUC__
|
||||||
# define DLL_EXPORT __attribute__((dllexport))
|
# define DLL_EXPORT __attribute__((dllexport))
|
||||||
# else
|
# else
|
||||||
@ -210,7 +211,7 @@ typedef boost::lock_guard<boost::recursive_mutex> TLockGuardRec;
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef VCMI_WINDOWS
|
||||||
# ifdef __GNUC__
|
# ifdef __GNUC__
|
||||||
# define DLL_IMPORT __attribute__((dllimport))
|
# define DLL_IMPORT __attribute__((dllimport))
|
||||||
# else
|
# else
|
||||||
|
@ -1601,22 +1601,20 @@ int CPlayerInterface::getLastIndex( std::string namePrefix)
|
|||||||
path gamesDir = VCMIDirs::get().userSavePath();
|
path gamesDir = VCMIDirs::get().userSavePath();
|
||||||
std::map<std::time_t, int> dates; //save number => datestamp
|
std::map<std::time_t, int> dates; //save number => datestamp
|
||||||
|
|
||||||
directory_iterator enddir;
|
const directory_iterator enddir;
|
||||||
if(!exists(gamesDir))
|
if(!exists(gamesDir))
|
||||||
create_directory(gamesDir);
|
create_directory(gamesDir);
|
||||||
|
else
|
||||||
for (directory_iterator dir(gamesDir); dir!=enddir; dir++)
|
for (directory_iterator dir(gamesDir); dir != enddir; ++dir)
|
||||||
{
|
{
|
||||||
if(is_regular(dir->status()))
|
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"))
|
if(starts_with(name, namePrefix) && ends_with(name, ".vcgm1"))
|
||||||
{
|
{
|
||||||
char nr = name[namePrefix.size()];
|
char nr = name[namePrefix.size()];
|
||||||
if(std::isdigit(nr))
|
if(std::isdigit(nr))
|
||||||
{
|
|
||||||
dates[last_write_time(dir->path())] = boost::lexical_cast<int>(nr);
|
dates[last_write_time(dir->path())] = boost::lexical_cast<int>(nr);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ Editor::Editor(QWidget *parent)
|
|||||||
{
|
{
|
||||||
// Setup default logging(enough for now)
|
// Setup default logging(enough for now)
|
||||||
console = new CConsoleHandler;
|
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();
|
logConfig.configureDefault();
|
||||||
|
|
||||||
preinitDLL(console);
|
preinitDLL(console);
|
||||||
|
@ -8,4 +8,13 @@
|
|||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QFile>
|
#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
|
||||||
|
}
|
@ -18,10 +18,10 @@ CLauncherDirs & CLauncherDirs::get()
|
|||||||
|
|
||||||
QString CLauncherDirs::downloadsPath()
|
QString CLauncherDirs::downloadsPath()
|
||||||
{
|
{
|
||||||
return QString::fromUtf8(VCMIDirs::get().userCachePath().c_str()) + "/downloads";
|
return pathToQString(VCMIDirs::get().userCachePath() / "downloads");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CLauncherDirs::modsPath()
|
QString CLauncherDirs::modsPath()
|
||||||
{
|
{
|
||||||
return QString::fromUtf8(VCMIDirs::get().userDataPath().c_str()) + "/Mods";
|
return pathToQString(VCMIDirs::get().userDataPath() / "Mods");
|
||||||
}
|
}
|
||||||
|
@ -13,15 +13,15 @@
|
|||||||
void MainWindow::load()
|
void MainWindow::load()
|
||||||
{
|
{
|
||||||
console = new CConsoleHandler;
|
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();
|
logConfig.configureDefault();
|
||||||
|
|
||||||
CResourceHandler::initialize();
|
CResourceHandler::initialize();
|
||||||
CResourceHandler::load("config/filesystem.json");
|
CResourceHandler::load("config/filesystem.json");
|
||||||
|
|
||||||
for (auto & string : VCMIDirs::get().dataPaths())
|
for (auto & string : VCMIDirs::get().dataPaths())
|
||||||
QDir::addSearchPath("icons", QString::fromUtf8(string.c_str()) + "/launcher/icons");
|
QDir::addSearchPath("icons", pathToQString(string / "launcher" / "icons"));
|
||||||
QDir::addSearchPath("icons", QString::fromUtf8(VCMIDirs::get().userDataPath().c_str()) + "/launcher/icons");
|
QDir::addSearchPath("icons", pathToQString(VCMIDirs::get().userDataPath() / "launcher" / "icons"));
|
||||||
|
|
||||||
settings.init();
|
settings.init();
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ MainWindow::~MainWindow()
|
|||||||
|
|
||||||
void MainWindow::on_startGameButon_clicked()
|
void MainWindow::on_startGameButon_clicked()
|
||||||
{
|
{
|
||||||
startExecutable(QString::fromUtf8(VCMIDirs::get().clientPath().c_str()));
|
startExecutable(pathToQString(VCMIDirs::get().clientPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::startExecutable(QString name)
|
void MainWindow::startExecutable(QString name)
|
||||||
|
@ -39,7 +39,7 @@ CModManager::CModManager(CModList * modList):
|
|||||||
|
|
||||||
QString CModManager::settingsPath()
|
QString CModManager::settingsPath()
|
||||||
{
|
{
|
||||||
return QString::fromUtf8(VCMIDirs::get().userConfigPath().c_str()) + "/modSettings.json";
|
return pathToQString(VCMIDirs::get().userConfigPath() / "modSettings.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CModManager::loadModSettings()
|
void CModManager::loadModSettings()
|
||||||
|
@ -46,9 +46,9 @@ void CSettingsView::loadSettings()
|
|||||||
for (auto entry : urls.Vector())
|
for (auto entry : urls.Vector())
|
||||||
ui->plainTextEditRepos->appendPlainText(QString::fromUtf8(entry.String().c_str()));
|
ui->plainTextEditRepos->appendPlainText(QString::fromUtf8(entry.String().c_str()));
|
||||||
|
|
||||||
ui->lineEditUserDataDir->setText(QString::fromUtf8(VCMIDirs::get().userDataPath().c_str()));
|
ui->lineEditUserDataDir->setText(pathToQString(VCMIDirs::get().userDataPath()));
|
||||||
ui->lineEditGameDir->setText(QString::fromUtf8(M_DATA_DIR));
|
ui->lineEditGameDir->setText(pathToQString(VCMIDirs::get().binaryPath()));
|
||||||
ui->lineEditTempDir->setText(QString::fromUtf8(VCMIDirs::get().userCachePath().c_str()));
|
ui->lineEditTempDir->setText(pathToQString(VCMIDirs::get().userCachePath()));
|
||||||
|
|
||||||
std::string encoding = settings["general"]["encoding"].String();
|
std::string encoding = settings["general"]["encoding"].String();
|
||||||
size_t encodingIndex = boost::range::find(knownEncodingsList, encoding) - knownEncodingsList;
|
size_t encodingIndex = boost::range::find(knownEncodingsList, encoding) - knownEncodingsList;
|
||||||
|
@ -35,7 +35,7 @@ extern "C" DLL_EXPORT void BattleAI_GetNewBattleAI(shared_ptr<CBattleGameInterfa
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<typename rett>
|
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(*TGetAIFun)(shared_ptr<rett>&);
|
||||||
typedef void(*TGetNameFun)(char*);
|
typedef void(*TGetNameFun)(char*);
|
||||||
@ -45,7 +45,13 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName)
|
|||||||
TGetAIFun getAI = nullptr;
|
TGetAIFun getAI = nullptr;
|
||||||
TGetNameFun getName = 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
|
// this is awful but it seems using shared libraries on some devices is even worse
|
||||||
if (dllname.find("libVCAI.so") != std::string::npos) {
|
if (dllname.find("libVCAI.so") != std::string::npos) {
|
||||||
getName = (TGetNameFun)VCAI_GetAiName;
|
getName = (TGetNameFun)VCAI_GetAiName;
|
||||||
@ -61,12 +67,12 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName)
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef VCMI_WINDOWS
|
||||||
HINSTANCE dll = LoadLibraryA(dllname.c_str());
|
HMODULE dll = LoadLibraryW(libpath.c_str());
|
||||||
if (dll)
|
if (dll)
|
||||||
{
|
{
|
||||||
getName = (TGetNameFun)GetProcAddress(dll,"GetAiName");
|
getName = (TGetNameFun)GetProcAddress(dll, "GetAiName");
|
||||||
getAI = (TGetAIFun)GetProcAddress(dll,methodName.c_str());
|
getAI = (TGetAIFun)GetProcAddress(dll, methodName.c_str());
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
void *dll = dlopen(dllname.c_str(), RTLD_LOCAL | RTLD_LAZY);
|
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
|
#endif
|
||||||
if (!dll)
|
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");
|
throw std::runtime_error("Cannot open dynamic library");
|
||||||
}
|
}
|
||||||
else if(!getName || !getAI)
|
else if(!getName || !getAI)
|
||||||
{
|
{
|
||||||
logGlobal->errorStream() << dllname << " does not export method " << methodName;
|
logGlobal->errorStream() << libpath << " does not export method " << methodName;
|
||||||
#ifdef _WIN32
|
#ifdef VCMI_WINDOWS
|
||||||
FreeLibrary(dll);
|
FreeLibrary(dll);
|
||||||
#else
|
#else
|
||||||
dlclose(dll);
|
dlclose(dll);
|
||||||
#endif
|
#endif
|
||||||
throw std::runtime_error("Cannot find method " + methodName);
|
throw std::runtime_error("Cannot find method " + methodName);
|
||||||
}
|
}
|
||||||
|
#endif // VCMI_ANDROID
|
||||||
#endif // __ANDROID__
|
|
||||||
|
|
||||||
getName(temp);
|
getName(temp);
|
||||||
logGlobal->infoStream() << "Loaded " << temp;
|
logGlobal->infoStream() << "Loaded " << temp;
|
||||||
@ -113,9 +118,8 @@ shared_ptr<rett> createAnyAI(std::string dllname, std::string methodName)
|
|||||||
logGlobal->infoStream() << "Opening " << dllname;
|
logGlobal->infoStream() << "Opening " << dllname;
|
||||||
const boost::filesystem::path filePath =
|
const boost::filesystem::path filePath =
|
||||||
VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(dllname);
|
VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(dllname);
|
||||||
// TODO: createAny Should take boost::filesystem::path in argument.
|
auto ret = createAny<rett>(filePath, methodName);
|
||||||
auto ret = createAny<rett>(filePath.string(), methodName);
|
ret->dllName = std::move(dllname);
|
||||||
ret->dllName = dllname;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
171
lib/VCMIDirs.cpp
171
lib/VCMIDirs.cpp
@ -30,6 +30,75 @@ void IVCMIDirs::init()
|
|||||||
#include <Shlobj.h>
|
#include <Shlobj.h>
|
||||||
#include <Shellapi.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
|
class VCMIDirsWIN32 : public IVCMIDirs
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -50,6 +119,9 @@ class VCMIDirsWIN32 : public IVCMIDirs
|
|||||||
std::string genHelpString() const override;
|
std::string genHelpString() const override;
|
||||||
|
|
||||||
void init() override;
|
void init() override;
|
||||||
|
protected:
|
||||||
|
boost::filesystem::path oldUserDataPath() const;
|
||||||
|
boost::filesystem::path oldUserSavePath() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
void VCMIDirsWIN32::init()
|
void VCMIDirsWIN32::init()
|
||||||
@ -57,15 +129,21 @@ void VCMIDirsWIN32::init()
|
|||||||
// Call base (init dirs)
|
// Call base (init dirs)
|
||||||
IVCMIDirs::init();
|
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))
|
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::is_empty(from))
|
||||||
{
|
{
|
||||||
|
if (bfs::current_path() == from)
|
||||||
|
bfs::current_path(to);
|
||||||
|
|
||||||
bfs::remove(from);
|
bfs::remove(from);
|
||||||
return; // Nothing to do here. Flies away.
|
return true; // Nothing to do here. Flies away.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bfs::is_directory(to))
|
if (!bfs::is_directory(to))
|
||||||
@ -103,23 +181,87 @@ void VCMIDirsWIN32::init()
|
|||||||
fileOp.lpszProgressTitle = nullptr;
|
fileOp.lpszProgressTitle = nullptr;
|
||||||
|
|
||||||
const int errorCode = SHFileOperationW(&fileOp);
|
const int errorCode = SHFileOperationW(&fileOp);
|
||||||
if (errorCode != 0); // TODO: Log error. User should try to move files.
|
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.
|
return false;
|
||||||
else if (!bfs::is_empty(from)); // TODO: Log warn. Some files not moved. User should try to move files.
|
else if (fileOp.fAnyOperationsAborted) // TODO: Log warn. User aborted operation. User should move files.
|
||||||
else // TODO: Log fact that we moved files succefully.
|
return false;
|
||||||
bfs::remove(from);
|
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
|
bfs::path VCMIDirsWIN32::userDataPath() const
|
||||||
{
|
{
|
||||||
wchar_t profileDir[MAX_PATH];
|
wchar_t profileDir[MAX_PATH];
|
||||||
|
|
||||||
// The user's profile folder. A typical path is C:\Users\username.
|
if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_MYDOCUMENTS, FALSE) != FALSE)
|
||||||
// FIXME: Applications should not create files or folders at this level;
|
return bfs::path(profileDir) / "My Games\\vcmi";
|
||||||
// they should put their data under the locations referred to by CSIDL_APPDATA or CSIDL_LOCAL_APPDATA.
|
|
||||||
|
return ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
bfs::path VCMIDirsWIN32::oldUserDataPath() const
|
||||||
|
{
|
||||||
|
wchar_t profileDir[MAX_PATH];
|
||||||
|
|
||||||
if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_PROFILE, FALSE) == FALSE) // WinAPI way failed
|
if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_PROFILE, FALSE) == FALSE) // WinAPI way failed
|
||||||
{
|
{
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1700
|
#if defined(_MSC_VER) && _MSC_VER >= 1700
|
||||||
@ -145,6 +287,8 @@ bfs::path VCMIDirsWIN32::userDataPath() const
|
|||||||
|
|
||||||
//return dataPaths()[0] ???;
|
//return dataPaths()[0] ???;
|
||||||
}
|
}
|
||||||
|
bfs::path VCMIDirsWIN32::oldUserSavePath() const { return userDataPath() / "Games"; }
|
||||||
|
|
||||||
bfs::path VCMIDirsWIN32::userCachePath() const { return userDataPath(); }
|
bfs::path VCMIDirsWIN32::userCachePath() const { return userDataPath(); }
|
||||||
bfs::path VCMIDirsWIN32::userConfigPath() const { return userDataPath() / "config"; }
|
bfs::path VCMIDirsWIN32::userConfigPath() const { return userDataPath() / "config"; }
|
||||||
|
|
||||||
@ -421,6 +565,9 @@ namespace VCMIDirs
|
|||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
{
|
{
|
||||||
|
std::locale::global(boost::locale::generator().generate("en_US.UTF-8"));
|
||||||
|
boost::filesystem::path::imbue(std::locale());
|
||||||
|
|
||||||
singleton.init();
|
singleton.init();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#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
|
class DLL_LINKAGE IVCMIDirs
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -13,9 +13,9 @@ ArchiveEntry::ArchiveEntry()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CArchiveLoader::CArchiveLoader(const std::string &mountPoint, const std::string & archive):
|
CArchiveLoader::CArchiveLoader(std::string _mountPoint, boost::filesystem::path _archive) :
|
||||||
archive(archive),
|
archive(std::move(_archive)),
|
||||||
mountPoint(mountPoint)
|
mountPoint(std::move(_mountPoint))
|
||||||
{
|
{
|
||||||
// Open archive file(.snd, .vid, .lod)
|
// Open archive file(.snd, .vid, .lod)
|
||||||
CFileInputStream fileStream(archive);
|
CFileInputStream fileStream(archive);
|
||||||
@ -25,28 +25,19 @@ CArchiveLoader::CArchiveLoader(const std::string &mountPoint, const std::string
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Retrieve file extension of archive in uppercase
|
// Retrieve file extension of archive in uppercase
|
||||||
CFileInfo fileInfo(archive);
|
const std::string ext = boost::to_upper_copy(archive.extension().string());
|
||||||
std::string ext = fileInfo.getExtension();
|
|
||||||
boost::to_upper(ext);
|
|
||||||
|
|
||||||
// Init the specific lod container format
|
// Init the specific lod container format
|
||||||
if(ext == ".LOD" || ext == ".PAC")
|
if(ext == ".LOD" || ext == ".PAC")
|
||||||
{
|
|
||||||
initLODArchive(mountPoint, fileStream);
|
initLODArchive(mountPoint, fileStream);
|
||||||
}
|
|
||||||
else if(ext == ".VID")
|
else if(ext == ".VID")
|
||||||
{
|
|
||||||
initVIDArchive(mountPoint, fileStream);
|
initVIDArchive(mountPoint, fileStream);
|
||||||
}
|
|
||||||
else if(ext == ".SND")
|
else if(ext == ".SND")
|
||||||
{
|
|
||||||
initSNDArchive(mountPoint, fileStream);
|
initSNDArchive(mountPoint, fileStream);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
throw std::runtime_error("LOD archive format unknown. Cannot deal with " + archive.string());
|
||||||
throw std::runtime_error("LOD archive format unknown. Cannot deal with " + archive);
|
|
||||||
}
|
logGlobal->traceStream() << ext << "Archive \""<<archive.filename()<<"\" loaded (" << entries.size() << " files found).";
|
||||||
logGlobal->traceStream() << ext << "Archive loaded, " << entries.size() << " files found";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStream & fileStream)
|
void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStream & fileStream)
|
||||||
@ -61,7 +52,7 @@ void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStr
|
|||||||
fileStream.seek(0x5c);
|
fileStream.seek(0x5c);
|
||||||
|
|
||||||
// Insert entries to list
|
// Insert entries to list
|
||||||
for(ui32 i = 0; i < totalFiles; i++)
|
for(ui32 i = 0; i < totalFiles; ++i)
|
||||||
{
|
{
|
||||||
char filename[16];
|
char filename[16];
|
||||||
reader.read(reinterpret_cast<ui8*>(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;
|
std::set<int> offsets;
|
||||||
|
|
||||||
// Insert entries to list
|
// Insert entries to list
|
||||||
for(ui32 i = 0; i < totalFiles; i++)
|
for(ui32 i = 0; i < totalFiles; ++i)
|
||||||
{
|
{
|
||||||
char filename[40];
|
char filename[40];
|
||||||
reader.read(reinterpret_cast<ui8*>(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();
|
ui32 totalFiles = reader.readUInt32();
|
||||||
|
|
||||||
// Insert entries to list
|
// Insert entries to list
|
||||||
for(ui32 i = 0; i < totalFiles; i++)
|
for(ui32 i = 0; i < totalFiles; ++i)
|
||||||
{
|
{
|
||||||
char filename[40];
|
char filename[40];
|
||||||
reader.read(reinterpret_cast<ui8*>(filename), 40);
|
reader.read(reinterpret_cast<ui8*>(filename), 40);
|
||||||
|
@ -55,7 +55,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @throws std::runtime_error if the archive wasn't found or if the archive isn't supported
|
* @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
|
/// Interface implementation
|
||||||
/// @see ISimpleResourceLoader
|
/// @see ISimpleResourceLoader
|
||||||
@ -87,7 +87,7 @@ private:
|
|||||||
void initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream);
|
void initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream);
|
||||||
|
|
||||||
/** The file path to the archive which is scanned and indexed. */
|
/** The file path to the archive which is scanned and indexed. */
|
||||||
std::string archive;
|
boost::filesystem::path archive;
|
||||||
|
|
||||||
std::string mountPoint;
|
std::string mountPoint;
|
||||||
|
|
||||||
|
@ -41,17 +41,17 @@ std::string CFileInfo::getPath() const
|
|||||||
std::string CFileInfo::getExtension() const
|
std::string CFileInfo::getExtension() const
|
||||||
{
|
{
|
||||||
// Get position of file extension dot
|
// 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);
|
return name.substr(dotPos);
|
||||||
else
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CFileInfo::getFilename() const
|
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);
|
return name.substr(found + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,9 +60,9 @@ std::string CFileInfo::getStem() const
|
|||||||
std::string rslt = name;
|
std::string rslt = name;
|
||||||
|
|
||||||
// Remove file extension
|
// 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);
|
rslt.erase(dotPos);
|
||||||
|
|
||||||
return rslt;
|
return rslt;
|
||||||
@ -70,18 +70,19 @@ std::string CFileInfo::getStem() const
|
|||||||
|
|
||||||
std::string CFileInfo::getBaseName() const
|
std::string CFileInfo::getBaseName() const
|
||||||
{
|
{
|
||||||
size_t begin = name.find_last_of("/");
|
size_t begin = name.find_last_of("/\\");
|
||||||
size_t end = name.find_last_of("/.");
|
size_t end = name.find_last_of(".");
|
||||||
|
|
||||||
if(end != std::string::npos && name[end] == '/')
|
|
||||||
end = std::string::npos;
|
|
||||||
|
|
||||||
if(begin == std::string::npos)
|
if(begin == std::string::npos)
|
||||||
begin = 0;
|
begin = 0;
|
||||||
else
|
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
|
EResType::Type CFileInfo::getType() const
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "CFileInfo.h"
|
#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);
|
open(file, start, size);
|
||||||
}
|
}
|
||||||
@ -18,14 +18,12 @@ CFileInputStream::~CFileInputStream()
|
|||||||
fileStream.close();
|
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())
|
if (fileStream.fail())
|
||||||
{
|
throw std::runtime_error("File " + file.string() + " isn't available.");
|
||||||
throw std::runtime_error("File " + file + " isn't available.");
|
|
||||||
}
|
|
||||||
|
|
||||||
dataStart = start;
|
dataStart = start;
|
||||||
dataSize = size;
|
dataSize = size;
|
||||||
|
@ -25,7 +25,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @see CFileInputStream::open
|
* @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.
|
* C-tor. Opens the specified file.
|
||||||
@ -88,11 +88,11 @@ private:
|
|||||||
*
|
*
|
||||||
* @throws std::runtime_error if file wasn't found
|
* @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 dataStart;
|
||||||
si64 dataSize;
|
si64 dataSize;
|
||||||
|
|
||||||
/** Native c++ input file stream object. */
|
/** Native c++ input file stream object. */
|
||||||
std::ifstream fileStream;
|
boost::filesystem::ifstream fileStream;
|
||||||
};
|
};
|
||||||
|
@ -4,9 +4,11 @@
|
|||||||
#include "CFileInfo.h"
|
#include "CFileInfo.h"
|
||||||
#include "CFileInputStream.h"
|
#include "CFileInputStream.h"
|
||||||
|
|
||||||
CFilesystemLoader::CFilesystemLoader(const std::string &mountPoint, const std::string & baseDirectory, size_t depth, bool initial):
|
namespace bfs = boost::filesystem;
|
||||||
baseDirectory(baseDirectory),
|
|
||||||
mountPoint(mountPoint),
|
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))
|
fileList(listFiles(mountPoint, depth, initial))
|
||||||
{
|
{
|
||||||
logGlobal->traceStream() << "Filesystem loaded, " << fileList.size() << " files found";
|
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));
|
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;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ boost::optional<std::string> CFilesystemLoader::getResourceName(const ResourceID
|
|||||||
{
|
{
|
||||||
assert(existsResource(resourceName));
|
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
|
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))
|
if (filter(file.first))
|
||||||
foundID.insert(file.first);
|
foundID.insert(file.first);
|
||||||
} return foundID;
|
}
|
||||||
|
return foundID;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CFilesystemLoader::createResource(std::string filename, bool update)
|
bool CFilesystemLoader::createResource(std::string filename, bool update)
|
||||||
@ -65,7 +68,7 @@ bool CFilesystemLoader::createResource(std::string filename, bool update)
|
|||||||
|
|
||||||
if (!update)
|
if (!update)
|
||||||
{
|
{
|
||||||
std::ofstream newfile (baseDirectory + "/" + filename);
|
bfs::ofstream newfile(baseDirectory / filename);
|
||||||
if (!newfile.good())
|
if (!newfile.good())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -73,49 +76,72 @@ bool CFilesystemLoader::createResource(std::string filename, bool update)
|
|||||||
return true;
|
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;
|
static const EResType::Type initArray[] = {
|
||||||
initialTypes.insert(EResType::DIRECTORY);
|
EResType::DIRECTORY,
|
||||||
initialTypes.insert(EResType::TEXT);
|
EResType::TEXT,
|
||||||
initialTypes.insert(EResType::ARCHIVE_LOD);
|
EResType::ARCHIVE_LOD,
|
||||||
initialTypes.insert(EResType::ARCHIVE_VID);
|
EResType::ARCHIVE_VID,
|
||||||
initialTypes.insert(EResType::ARCHIVE_SND);
|
EResType::ARCHIVE_SND,
|
||||||
initialTypes.insert(EResType::ARCHIVE_ZIP);
|
EResType::ARCHIVE_ZIP };
|
||||||
|
static const std::set<EResType::Type> initialTypes(initArray, initArray + ARRAY_COUNT(initArray));
|
||||||
|
|
||||||
assert(boost::filesystem::is_directory(baseDirectory));
|
assert(bfs::is_directory(baseDirectory));
|
||||||
std::unordered_map<ResourceID, std::string> fileList;
|
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;
|
bfs::recursive_directory_iterator enddir;
|
||||||
boost::filesystem::recursive_directory_iterator it(baseDirectory, boost::filesystem::symlink_option::recurse);
|
bfs::recursive_directory_iterator it(baseDirectory, bfs::symlink_option::recurse);
|
||||||
|
|
||||||
for(; it != enddir; ++it)
|
for(; it != enddir; ++it)
|
||||||
{
|
{
|
||||||
EResType::Type type;
|
EResType::Type type;
|
||||||
|
|
||||||
if (boost::filesystem::is_directory(it->status()))
|
if (bfs::is_directory(it->status()))
|
||||||
{
|
{
|
||||||
path.resize(it.level()+1);
|
path.resize(it.level() + 1);
|
||||||
path.back() = it->path().leaf().string();
|
path.back() = it->path().filename();
|
||||||
// don't iterate into directory if depth limit reached
|
// don't iterate into directory if depth limit reached
|
||||||
it.no_push(depth <= it.level());
|
it.no_push(depth <= it.level());
|
||||||
|
|
||||||
type = EResType::DIRECTORY;
|
type = EResType::DIRECTORY;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
type = EResTypeHelper::getTypeFromExtension(boost::filesystem::extension(*it));
|
type = EResTypeHelper::getTypeFromExtension(it->path().extension().string());
|
||||||
|
|
||||||
if (!initial || vstd::contains(initialTypes, type))
|
if (!initial || vstd::contains(initialTypes, type))
|
||||||
{
|
{
|
||||||
//reconstruct relative filename (not possible via boost AFAIK)
|
//reconstruct relative filename (not possible via boost AFAIK)
|
||||||
std::string filename;
|
bfs::path filename;
|
||||||
for (size_t i=0; i<it.level() && i<path.size(); i++)
|
const size_t iterations = std::min((size_t)it.level(), path.size());
|
||||||
filename += path[i] + '/';
|
if (iterations)
|
||||||
filename += it->path().leaf().string();
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @throws std::runtime_error if the base directory is not a directory or if it is not available
|
* @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
|
/// Interface implementation
|
||||||
/// @see ISimpleResourceLoader
|
/// @see ISimpleResourceLoader
|
||||||
@ -39,11 +39,11 @@ public:
|
|||||||
std::string getMountPoint() const override;
|
std::string getMountPoint() const override;
|
||||||
bool createResource(std::string filename, bool update = false) override;
|
bool createResource(std::string filename, bool update = false) override;
|
||||||
boost::optional<std::string> getResourceName(const ResourceID & resourceName) const 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:
|
private:
|
||||||
/** The base directory which is scanned and indexed. */
|
/** The base directory which is scanned and indexed. */
|
||||||
std::string baseDirectory;
|
boost::filesystem::path baseDirectory;
|
||||||
|
|
||||||
std::string mountPoint;
|
std::string mountPoint;
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ private:
|
|||||||
* key = ResourceID for resource loader
|
* key = ResourceID for resource loader
|
||||||
* value = name that can be used to access file
|
* 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.
|
* 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
|
* @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.
|
* 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;
|
||||||
};
|
};
|
||||||
|
@ -130,19 +130,18 @@ ISimpleResourceLoader * CResourceHandler::createInitial()
|
|||||||
auto filename = loader->getResourceName(ID);
|
auto filename = loader->getResourceName(ID);
|
||||||
if (filename)
|
if (filename)
|
||||||
{
|
{
|
||||||
auto dir = new CFilesystemLoader(URI + "/", *filename, depth, true);
|
auto dir = new CFilesystemLoader(URI + '/', *filename, depth, true);
|
||||||
initialLoader->addLoader(dir, false);
|
initialLoader->addLoader(dir, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: CFilesystemLoader: Should take boost::filesystem::path in argument
|
|
||||||
for (auto & path : VCMIDirs::get().dataPaths())
|
for (auto & path : VCMIDirs::get().dataPaths())
|
||||||
{
|
{
|
||||||
if (boost::filesystem::is_directory(path)) // some of system-provided paths may not exist
|
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("CONFIG", 0);// look for configs
|
||||||
recurseInDir("DATA", 0); // look for archives
|
recurseInDir("DATA", 0); // look for archives
|
||||||
@ -167,10 +166,9 @@ void CResourceHandler::initialize()
|
|||||||
// |-saves
|
// |-saves
|
||||||
// |-config
|
// |-config
|
||||||
|
|
||||||
// TODO: CFilesystemLoader should take boost::filesystem::path
|
|
||||||
knownLoaders["root"] = new CFilesystemList();
|
knownLoaders["root"] = new CFilesystemList();
|
||||||
knownLoaders["saves"] = new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath().string());
|
knownLoaders["saves"] = new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath());
|
||||||
knownLoaders["config"] = new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath().string());
|
knownLoaders["config"] = new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath());
|
||||||
|
|
||||||
auto localFS = new CFilesystemList();
|
auto localFS = new CFilesystemList();
|
||||||
localFS->addLoader(knownLoaders["saves"], true);
|
localFS->addLoader(knownLoaders["saves"], true);
|
||||||
|
@ -3,10 +3,7 @@
|
|||||||
|
|
||||||
#include "../CConfigHandler.h"
|
#include "../CConfigHandler.h"
|
||||||
|
|
||||||
CBasicLogConfigurator::CBasicLogConfigurator(const boost::filesystem::path & filePath, CConsoleHandler * const console) :
|
CBasicLogConfigurator::CBasicLogConfigurator(boost::filesystem::path filePath, CConsoleHandler * const console) :
|
||||||
filePath(filePath), console(console), appendToLogFile(false) {}
|
|
||||||
|
|
||||||
CBasicLogConfigurator::CBasicLogConfigurator(boost::filesystem::path && filePath, CConsoleHandler * const console) :
|
|
||||||
filePath(std::move(filePath)), console(console), appendToLogFile(false) {}
|
filePath(std::move(filePath)), console(console), appendToLogFile(false) {}
|
||||||
|
|
||||||
void CBasicLogConfigurator::configureDefault()
|
void CBasicLogConfigurator::configureDefault()
|
||||||
|
@ -22,8 +22,7 @@ class JsonNode;
|
|||||||
class DLL_LINKAGE CBasicLogConfigurator
|
class DLL_LINKAGE CBasicLogConfigurator
|
||||||
{
|
{
|
||||||
public:
|
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.
|
/// 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.
|
/// Doesn't throw, but logs on success or fault.
|
||||||
|
@ -1,14 +1,30 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "CLogger.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";
|
const std::string CLoggerDomain::DOMAIN_GLOBAL = "global";
|
||||||
|
|
||||||
CLoggerDomain::CLoggerDomain(const std::string & name) : name(name)
|
CLoggerDomain::CLoggerDomain(std::string name) : name(std::move(name))
|
||||||
{
|
|
||||||
if(name.empty())
|
|
||||||
throw std::runtime_error("Logger domain cannot be empty.");
|
|
||||||
}
|
|
||||||
CLoggerDomain::CLoggerDomain(std::string && name) : name(std::move(name))
|
|
||||||
{
|
{
|
||||||
if (this->name.empty())
|
if (this->name.empty())
|
||||||
throw std::runtime_error("Logger domain cannot be 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)
|
void CLogConsoleTarget::write(const LogRecord & record)
|
||||||
{
|
{
|
||||||
if(threshold > record.level) return;
|
if(threshold > record.level)
|
||||||
|
return;
|
||||||
|
|
||||||
std::string message = formatter.format(record);
|
std::string message = formatter.format(record);
|
||||||
|
|
||||||
#ifdef VCMI_ANDROID
|
#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
|
#endif
|
||||||
|
|
||||||
bool printToStdErr = record.level >= ELogLevel::WARN;
|
const bool printToStdErr = record.level >= ELogLevel::WARN;
|
||||||
if(console)
|
if(console)
|
||||||
{
|
{
|
||||||
const EConsoleTextColor::EConsoleTextColor textColor =
|
const EConsoleTextColor::EConsoleTextColor textColor =
|
||||||
@ -340,22 +357,12 @@ void CLogConsoleTarget::setFormatter(const CLogFormatter & formatter) { this->fo
|
|||||||
const CColorMapping & CLogConsoleTarget::getColorMapping() const { return colorMapping; }
|
const CColorMapping & CLogConsoleTarget::getColorMapping() const { return colorMapping; }
|
||||||
void CLogConsoleTarget::setColorMapping(const CColorMapping & colorMapping) { this->colorMapping = colorMapping; }
|
void CLogConsoleTarget::setColorMapping(const CColorMapping & colorMapping) { this->colorMapping = colorMapping; }
|
||||||
|
|
||||||
CLogFileTarget::CLogFileTarget(const boost::filesystem::path & filePath, bool append /*= true*/)
|
CLogFileTarget::CLogFileTarget(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*/)
|
|
||||||
: file(std::move(filePath), append ? std::ios_base::app : std::ios_base::out)
|
: file(std::move(filePath), append ? std::ios_base::app : std::ios_base::out)
|
||||||
{
|
{
|
||||||
formatter.setPattern("%d %l %n [%t] - %m");
|
formatter.setPattern("%d %l %n [%t] - %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
CLogFileTarget::~CLogFileTarget()
|
|
||||||
{
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CLogFileTarget::write(const LogRecord & record)
|
void CLogFileTarget::write(const LogRecord & record)
|
||||||
{
|
{
|
||||||
TLockGuard _(mx);
|
TLockGuard _(mx);
|
||||||
|
@ -19,15 +19,19 @@ class ILogTarget;
|
|||||||
|
|
||||||
namespace ELogLevel
|
namespace ELogLevel
|
||||||
{
|
{
|
||||||
enum ELogLevel
|
enum ELogLevel
|
||||||
{
|
{
|
||||||
NOT_SET = 0,
|
NOT_SET = 0,
|
||||||
TRACE,
|
TRACE,
|
||||||
DEBUG,
|
DEBUG,
|
||||||
INFO,
|
INFO,
|
||||||
WARN,
|
WARN,
|
||||||
ERROR
|
ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
int toAndroid(ELogLevel logLevel);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The class CLoggerDomain provides convenient access to super domains from a sub domain.
|
/// The class CLoggerDomain provides convenient access to super domains from a sub domain.
|
||||||
@ -36,8 +40,7 @@ class DLL_LINKAGE CLoggerDomain
|
|||||||
public:
|
public:
|
||||||
/// Constructs a CLoggerDomain with the domain designated by name.
|
/// 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".
|
/// 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;
|
const std::string& getName() const;
|
||||||
CLoggerDomain getParent() const;
|
CLoggerDomain getParent() const;
|
||||||
@ -288,9 +291,7 @@ class DLL_LINKAGE CLogFileTarget : public ILogTarget
|
|||||||
public:
|
public:
|
||||||
/// Constructs a CLogFileTarget and opens the file designated by file_path. If the append parameter is true, the file
|
/// 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.
|
/// 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);
|
||||||
explicit CLogFileTarget(boost::filesystem::path && file_path, bool append = true);
|
|
||||||
~CLogFileTarget();
|
|
||||||
|
|
||||||
const CLogFormatter & getFormatter() const;
|
const CLogFormatter & getFormatter() const;
|
||||||
void setFormatter(const CLogFormatter & formatter);
|
void setFormatter(const CLogFormatter & formatter);
|
||||||
|
@ -368,19 +368,19 @@ void ERMInterpreter::scanForScripts()
|
|||||||
{
|
{
|
||||||
using namespace boost::filesystem;
|
using namespace boost::filesystem;
|
||||||
//parser checking
|
//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;
|
return;
|
||||||
}
|
}
|
||||||
directory_iterator enddir;
|
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()))
|
if(is_regular(dir->status()))
|
||||||
{
|
{
|
||||||
std::string name = dir->path().leaf().string();
|
const std::string ext = boost::to_upper_copy(dir->path().extension().string());
|
||||||
if( boost::algorithm::ends_with(name, ".erm") ||
|
if (ext == ".ERM" || ext == ".VERM")
|
||||||
boost::algorithm::ends_with(name, ".verm") )
|
|
||||||
{
|
{
|
||||||
ERMParser ep(dir->path().string());
|
ERMParser ep(dir->path().string());
|
||||||
FileInfo * finfo = new FileInfo;
|
FileInfo * finfo = new FileInfo;
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
CVcmiTestConfig::CVcmiTestConfig()
|
CVcmiTestConfig::CVcmiTestConfig()
|
||||||
{
|
{
|
||||||
console = new CConsoleHandler;
|
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();
|
logConfig.configureDefault();
|
||||||
preinitDLL(console);
|
preinitDLL(console);
|
||||||
settings.init();
|
settings.init();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user