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