diff --git a/CMakeLists.txt b/CMakeLists.txt index a33c5dc6e..7f349e223 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,7 @@ set(OENGINE_OGRE ) set(OENGINE_GUI + ${LIBDIR}/openengine/gui/loglistener.cpp ${LIBDIR}/openengine/gui/manager.cpp ${LIBDIR}/openengine/gui/layout.hpp ) @@ -224,6 +225,9 @@ endif () set(BOOST_COMPONENTS system filesystem program_options) +if(WIN32) + set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) +endif(WIN32) IF(BOOST_STATIC) set(Boost_USE_STATIC_LIBS ON) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 5cf8f8a89..fb4c09b91 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -41,11 +41,11 @@ Launcher::MainDialog::MainDialog(QWidget *parent) // Check if the font is installed if (!fonts.contains("EB Garamond")) { - QString font = QString::fromStdString(mCfgMgr.getGlobalDataPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf"); + QString font = QString::fromUtf8(mCfgMgr.getGlobalDataPath().string().c_str()) + QString("resources/mygui/EBGaramond-Regular.ttf"); file.setFileName(font); if (!file.exists()) { - font = QString::fromStdString(mCfgMgr.getLocalPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf"); + font = QString::fromUtf8(mCfgMgr.getLocalPath().string().c_str()) + QString("resources/mygui/EBGaramond-Regular.ttf"); } fontDatabase.addApplicationFont(font); @@ -243,7 +243,7 @@ bool Launcher::MainDialog::showFirstRunDialog() } // Create the file if it doesn't already exist, else the importer will fail - QString path = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + QString("openmw.cfg"); + QString path = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()) + QString("openmw.cfg"); QFile file(path); if (!file.exists()) { @@ -358,7 +358,7 @@ bool Launcher::MainDialog::setupLauncherSettings() { mLauncherSettings.setMultiValueEnabled(true); - QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); + QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QStringList paths; paths.append(QString("launcher.cfg")); @@ -464,8 +464,8 @@ bool Launcher::expansions(Launcher::UnshieldThread& cd) bool Launcher::MainDialog::setupGameSettings() { - QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); - QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); + QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); + QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); // Load the user config file first, separately // So we can write it properly, uncontaminated @@ -594,7 +594,7 @@ bool Launcher::MainDialog::setupGameSettings() while(expansions(cd)); - selectedFile = QString::fromStdString(cd.GetMWEsmPath()); + selectedFile = QString::fromUtf8(cd.GetMWEsmPath().c_str()); } #endif // WIN32 @@ -615,8 +615,8 @@ bool Launcher::MainDialog::setupGraphicsSettings() { mGraphicsSettings.setMultiValueEnabled(false); - QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); - QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); + QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); + QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); QFile localDefault(QString("settings-default.cfg")); QFile globalDefault(globalPath + QString("settings-default.cfg")); @@ -702,7 +702,7 @@ bool Launcher::MainDialog::writeSettings() mGraphicsPage->saveSettings(); mDataFilesPage->saveSettings(); - QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); + QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QDir dir(userPath); if (!dir.exists()) { diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index e7e5cf1ea..2f3dd9a45 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -45,7 +45,8 @@ void Launcher::GameSettings::validatePaths() Files::PathContainer dataDirs; foreach (const QString &path, paths) { - dataDirs.push_back(Files::PathContainer::value_type(path.toStdString())); + QByteArray bytes = path.toUtf8(); + dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length()))); } // Parse the data dirs to convert the tokenized paths @@ -53,7 +54,7 @@ void Launcher::GameSettings::validatePaths() mDataDirs.clear(); for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { - QString path = QString::fromStdString(it->string()); + QString path = QString::fromUtf8(it->string().c_str()); path.remove(QChar('\"')); QDir dir(path); @@ -68,12 +69,13 @@ void Launcher::GameSettings::validatePaths() return; dataDirs.clear(); - dataDirs.push_back(Files::PathContainer::value_type(local.toStdString())); + QByteArray bytes = local.toUtf8(); + dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length()))); mCfgMgr.processPaths(dataDirs); if (!dataDirs.empty()) { - QString path = QString::fromStdString(dataDirs.front().string()); + QString path = QString::fromUtf8(dataDirs.front().string().c_str()); path.remove(QChar('\"')); QDir dir(path); diff --git a/apps/launcher/unshieldthread.cpp b/apps/launcher/unshieldthread.cpp index 52f935710..3d0f2e5da 100644 --- a/apps/launcher/unshieldthread.cpp +++ b/apps/launcher/unshieldthread.cpp @@ -1,6 +1,6 @@ #include "unshieldthread.hpp" -#include +#include #include namespace bfs = boost::filesystem; @@ -49,7 +49,7 @@ namespace std::string read_to_string(const bfs::path& path) { - std::ifstream strstream(path.c_str(), std::ios::in | std::ios::binary); + bfs::ifstream strstream(path, std::ios::in | std::ios::binary); std::string str; strstream.seekg(0, std::ios::end); @@ -201,7 +201,7 @@ namespace add_setting("Archives", "Archive 1", "Bloodmoon.bsa", ini); } - std::ofstream inistream(ini_path.c_str()); + bfs::ofstream inistream((ini_path)); inistream << ini; inistream.close(); } diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 648ab3ebe..3a52592ae 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -1,6 +1,5 @@ #include "importer.hpp" -#include -#include + #include #include #include @@ -9,6 +8,10 @@ #include #include +#include +#include + +namespace bfs = boost::filesystem; MwIniImporter::MwIniImporter() : mVerbose(false) @@ -661,7 +664,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filenam std::string section(""); MwIniImporter::multistrmap map; - boost::iostreams::streamfile(filename.c_str()); + bfs::ifstream file((bfs::path(filename))); ToUTF8::Utf8Encoder encoder(mEncoding); std::string line; @@ -674,6 +677,10 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filenam line = line.substr(0, line.length()-1); } + if(line.empty()) { + continue; + } + if(line[0] == '[') { int pos = line.find(']'); if(pos < 2) { @@ -690,10 +697,6 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filenam line = line.substr(0,comment_pos); } - if(line.empty()) { - continue; - } - int pos = line.find("="); if(pos < 1) { continue; @@ -720,7 +723,7 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::string& filenam std::cout << "load cfg file: " << filename << std::endl; MwIniImporter::multistrmap map; - boost::iostreams::streamfile(filename.c_str()); + bfs::ifstream file((bfs::path(filename))); std::string line; while (std::getline(file, line)) { @@ -858,7 +861,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co } } -void MwIniImporter::writeToFile(boost::iostreams::stream &out, const multistrmap &cfg) { +void MwIniImporter::writeToFile(std::ostream &out, const multistrmap &cfg) { for(multistrmap::const_iterator it=cfg.begin(); it != cfg.end(); ++it) { for(std::vector::const_iterator entry=it->second.begin(); entry != it->second.end(); ++entry) { diff --git a/apps/mwiniimporter/importer.hpp b/apps/mwiniimporter/importer.hpp index 784980e09..72b14ba75 100644 --- a/apps/mwiniimporter/importer.hpp +++ b/apps/mwiniimporter/importer.hpp @@ -1,12 +1,11 @@ #ifndef MWINIIMPORTER_IMPORTER #define MWINIIMPORTER_IMPORTER 1 -#include -#include #include #include #include #include +#include #include @@ -24,7 +23,7 @@ class MwIniImporter { void mergeFallback(multistrmap &cfg, const multistrmap &ini) const; void importGameFiles(multistrmap &cfg, const multistrmap &ini) const; void importArchives(multistrmap &cfg, const multistrmap &ini) const; - static void writeToFile(boost::iostreams::stream &out, const multistrmap &cfg); + static void writeToFile(std::ostream &out, const multistrmap &cfg); private: static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); diff --git a/apps/mwiniimporter/main.cpp b/apps/mwiniimporter/main.cpp index 364a6b1a4..c2408a554 100644 --- a/apps/mwiniimporter/main.cpp +++ b/apps/mwiniimporter/main.cpp @@ -1,14 +1,58 @@ #include "importer.hpp" -#include #include + #include #include +#include +#include namespace bpo = boost::program_options; +namespace bfs = boost::filesystem; +#ifndef _WIN32 int main(int argc, char *argv[]) { +#else + +// Include on Windows only +#include + +class utf8argv +{ +public: + + utf8argv(int argc, wchar_t *wargv[]) + { + args.reserve(argc); + argv = new const char *[argc]; + + for (int i = 0; i < argc; ++i) { + args.push_back(boost::locale::conv::utf_to_utf(wargv[i])); + argv[i] = args.back().c_str(); + } + } + + ~utf8argv() { delete[] argv; } + char **get() const { return const_cast(argv); } + +private: + + const char **argv; + std::vector args; +}; + +/* The only way to pass Unicode on Winodws with CLI is to use wide + characters interface which presents UTF-16 encoding. The rest of + OpenMW application stack assumes UTF-8 encoding, therefore this + conversion. + For boost::filesystem::path::imbue see components/files/windowspath.cpp +*/ +int wmain(int argc, wchar_t *wargv[]) { + utf8argv converter(argc, wargv); + char **argv = converter.get(); + boost::filesystem::path::imbue(boost::locale::generator().generate("")); +#endif bpo::options_description desc("Syntax: mwiniimporter inifile configfile\nAllowed options"); bpo::positional_options_description p_desc; desc.add_options() @@ -94,7 +138,7 @@ int main(int argc, char *argv[]) { } std::cout << "write to: " << outputFile << std::endl; - boost::iostreams::stream file(outputFile); + bfs::ofstream file((bfs::path(outputFile))); importer.writeToFile(file, cfg); return 0; diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 35551f97b..4576432e1 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -145,6 +145,10 @@ if(WIN32) endif(WIN32) set(BOOST_COMPONENTS system filesystem program_options thread wave) +if(WIN32) + set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) +endif(WIN32) + find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index eb93d9047..066c76734 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -27,7 +27,9 @@ void CSMDoc::OpenSaveStage::perform (int stage, Messages& messages) { mState.start (mDocument, mProjectFile); - mState.getStream().open ((mProjectFile ? mState.getPath() : mState.getTmpPath()).string().c_str()); + mState.getStream().open ( + mProjectFile ? mState.getPath() : mState.getTmpPath(), + std::ios::binary); if (!mState.getStream().is_open()) throw std::runtime_error ("failed to open stream for saving"); @@ -260,4 +262,4 @@ void CSMDoc::FinalSavingStage::perform (int stage, Messages& messages) mDocument.getUndoStack().setClean(); } -} \ No newline at end of file +} diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp index 874214822..07c42a490 100644 --- a/apps/opencs/model/doc/savingstate.cpp +++ b/apps/opencs/model/doc/savingstate.cpp @@ -47,7 +47,7 @@ const boost::filesystem::path& CSMDoc::SavingState::getTmpPath() const return mTmpPath; } -std::ofstream& CSMDoc::SavingState::getStream() +boost::filesystem::ofstream& CSMDoc::SavingState::getStream() { return mStream; } @@ -60,4 +60,4 @@ ESM::ESMWriter& CSMDoc::SavingState::getWriter() bool CSMDoc::SavingState::isProjectFile() const { return mProjectFile; -} \ No newline at end of file +} diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp index 6b4565584..7b0c37386 100644 --- a/apps/opencs/model/doc/savingstate.hpp +++ b/apps/opencs/model/doc/savingstate.hpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -20,7 +21,7 @@ namespace CSMDoc boost::filesystem::path mPath; boost::filesystem::path mTmpPath; ToUTF8::Utf8Encoder mEncoder; - std::ofstream mStream; + boost::filesystem::ofstream mStream; ESM::ESMWriter mWriter; boost::filesystem::path mProjectPath; bool mProjectFile; @@ -39,7 +40,7 @@ namespace CSMDoc const boost::filesystem::path& getTmpPath() const; - std::ofstream& getStream(); + boost::filesystem::ofstream& getStream(); ESM::ESMWriter& getWriter(); @@ -50,4 +51,4 @@ namespace CSMDoc } -#endif \ No newline at end of file +#endif diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index e791ab891..9e959a986 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -83,6 +83,10 @@ add_openmw_dir (mwbase # Main executable set(BOOST_COMPONENTS system filesystem program_options thread wave) +if(WIN32) + set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) +endif(WIN32) + find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) IF(OGRE_STATIC) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 26ff7e96e..24343577e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -236,7 +236,7 @@ void OMW::Engine::addArchive (const std::string& archive) { // Set resource dir void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir) { - mResDir = boost::filesystem::system_complete(parResDir); + mResDir = parResDir; } // Set start cell name (only interiors for now) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index d51d4298f..237d145a2 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -1,5 +1,8 @@ #include "console.hpp" +#include +#include + #include #include @@ -194,7 +197,8 @@ namespace MWGui void Console::executeFile (const std::string& path) { - std::ifstream stream (path.c_str()); + namespace bfs = boost::filesystem; + bfs::ifstream stream ((bfs::path(path))); if (!stream.is_open()) printError ("failed to open file: " + path); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 43972e9f9..dafb8323a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -12,6 +12,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" @@ -185,7 +187,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot else slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); - std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); + boost::filesystem::ofstream stream (slot->mPath, std::ios::binary); ESM::ESMWriter writer; diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 25b006fb3..0958c8f0c 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -25,6 +25,9 @@ #include +#include +#include + #include "../files/constrainedfiledatastream.hpp" using namespace std; @@ -72,7 +75,8 @@ void BSAFile::readHeader() */ assert(!isLoaded); - std::ifstream input(filename.c_str(), std::ios_base::binary); + namespace bfs = boost::filesystem; + bfs::ifstream input(bfs::path(filename), std::ios_base::binary); // Total archive size size_t fsize = 0; diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 761b7ca5a..ffa911b44 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -1,12 +1,12 @@ #include "configurationmanager.hpp" #include -#include #include #include #include #include +#include /** * \namespace Files @@ -125,7 +125,7 @@ void ConfigurationManager::loadConfig(const boost::filesystem::path& path, { std::cout << "Loading config file: " << cfgFile.string() << "... "; - std::ifstream configFileStream(cfgFile.string().c_str()); + boost::filesystem::ifstream configFileStream(cfgFile); if (configFileStream.is_open()) { boost::program_options::store(boost::program_options::parse_config_file( diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp index 06ee9fb4e..4cdeec043 100644 --- a/components/files/lowlevelfile.cpp +++ b/components/files/lowlevelfile.cpp @@ -206,6 +206,8 @@ size_t LowLevelFile::read (void * data, size_t size) } #elif FILE_API == FILE_API_WIN32 + +#include /* * * Implementation of LowLevelFile methods using Win32 API calls @@ -227,9 +229,10 @@ void LowLevelFile::open (char const * filename) { assert (mHandle == INVALID_HANDLE_VALUE); - HANDLE handle = CreateFileA (filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + std::wstring wname = boost::locale::conv::utf_to_utf(filename); + HANDLE handle = CreateFileW (wname.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - if (handle == NULL) + if (handle == INVALID_HANDLE_VALUE) { std::ostringstream os; os << "Failed to open '" << filename << "' for reading."; diff --git a/components/files/multidircollection.cpp b/components/files/multidircollection.cpp index 347de96a6..1abbae3ae 100644 --- a/components/files/multidircollection.cpp +++ b/components/files/multidircollection.cpp @@ -60,10 +60,10 @@ namespace Files { boost::filesystem::path path = *dirIter; - if (!equal (extension, boost::filesystem::path (path.extension()).string())) + if (!equal (extension, path.extension().string())) continue; - std::string filename = boost::filesystem::path (path.filename()).string(); + std::string filename = path.filename().string(); TIter result = mFiles.find (filename); diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index ea1ca56d3..6b58304a0 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -10,8 +10,12 @@ #pragma comment(lib, "Shlwapi.lib") +#include +namespace bconv = boost::locale::conv; + /** * FIXME: Someone with Windows system should check this and correct if necessary + * FIXME: MAX_PATH is irrelevant for extended-length paths, i.e. \\?\... */ /** @@ -23,22 +27,29 @@ namespace Files WindowsPath::WindowsPath(const std::string& application_name) : mName(application_name) { + /* Since on Windows boost::path.string() returns string of narrow + characters in local encoding, it is required to path::imbue() + with UTF-8 encoding (generated for empty name from boost::locale) + to handle Unicode in platform-agnostic way using std::string. + + See boost::filesystem and boost::locale reference for details. + */ + boost::filesystem::path::imbue(boost::locale::generator().generate("")); } boost::filesystem::path WindowsPath::getUserConfigPath() const { boost::filesystem::path userPath("."); - TCHAR path[MAX_PATH]; + WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); - if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path))) + if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path))) { - PathAppend(path, TEXT("My Games")); - userPath = boost::filesystem::path(path); + userPath = boost::filesystem::path(bconv::utf_to_utf(path)); } - return userPath / mName; + return userPath / "My Games" / mName; } boost::filesystem::path WindowsPath::getUserDataPath() const @@ -51,12 +62,12 @@ boost::filesystem::path WindowsPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("."); - TCHAR path[MAX_PATH]; + WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); - if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, 0, path))) + if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, 0, path))) { - globalPath = boost::filesystem::path(path); + globalPath = boost::filesystem::path(bconv::utf_to_utf(path)); } return globalPath / mName; diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp index 01b8764c2..77dbcb1ee 100644 --- a/components/ogreinit/ogreinit.cpp +++ b/components/ogreinit/ogreinit.cpp @@ -1,11 +1,15 @@ #include "ogreinit.hpp" #include +#include +#include +#include #include #include #include #include +#include #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE #include @@ -13,8 +17,54 @@ #include +#include +#include + #include "ogreplugin.hpp" +namespace bfs = boost::filesystem; + +namespace +{ + /** \brief Custom Ogre::LogListener interface implementation being + able to portably handle UTF-8 encoded path. + + Effectively this is used in conjunction with default listener, + but since on every message messageLogged() set 'skip' flag to + true, there should be no troubles sharing same file. + */ + class LogListener : public Ogre::LogListener + { + bfs::ofstream file; + char buffer[16]; + + + public: + + LogListener(const std::string &path) + : file((bfs::path(path))) + { + memset(buffer, sizeof(buffer), 0); + } + + void timestamp() + { + int local = time(0) % 86400; + int sec = local % 60; + int min = (local / 60) % 60; + int hrs = local / 3600; + sprintf(buffer, "%02d:%02d:%02d: ", hrs, min, sec); + } + + virtual void messageLogged(const std::string &msg, Ogre::LogMessageLevel lvl, bool mask, const std::string &logName, bool &skip) + { + timestamp(); + file << buffer << msg << std::endl; + skip = true; + } + }; +} + namespace OgreInit { @@ -43,6 +93,11 @@ namespace OgreInit new Ogre::LogManager; Ogre::Log *log = Ogre::LogManager::getSingleton().createLog(logPath); + #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + // Use custom listener only on Windows + log->addListener(new LogListener(logPath)); + #endif + // Disable logging to cout/cerr log->setDebugOutputEnabled(false); @@ -141,11 +196,6 @@ namespace OgreInit pluginDir = OGRE_PLUGIN_DIR_REL; #endif } - - boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir)); - - pluginDir = absPluginPath.string(); - Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot); Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot); diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index 13015e39c..0def0afdb 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -1,13 +1,19 @@ #include "settings.hpp" -#include #include +#include +#include #include #include +#include + +#include using namespace Settings; +namespace bfs = boost::filesystem; + Ogre::ConfigFile Manager::mFile = Ogre::ConfigFile(); Ogre::ConfigFile Manager::mDefaultFile = Ogre::ConfigFile(); CategorySettingVector Manager::mChangedSettings = CategorySettingVector(); @@ -15,17 +21,19 @@ CategorySettingValueMap Manager::mNewSettings = CategorySettingValueMap(); void Manager::loadUser (const std::string& file) { - mFile.load(file); + Ogre::DataStreamPtr stream = openConstrainedFileDataStream(file.c_str()); + mFile.load(stream); } void Manager::loadDefault (const std::string& file) { - mDefaultFile.load(file); + Ogre::DataStreamPtr stream = openConstrainedFileDataStream(file.c_str()); + mDefaultFile.load(stream); } void Manager::saveUser(const std::string& file) { - std::fstream fout(file.c_str(), std::ios::out); + bfs::ofstream fout((bfs::path(file))); Ogre::ConfigFile::SectionIterator seci = mFile.getSectionIterator(); diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index d0ea4b7fb..423c3971a 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -1,7 +1,8 @@ #include "translation.hpp" -#include -#include +#include + +#include namespace Translation { @@ -28,9 +29,8 @@ namespace Translation if (dataFileCollections.getCollection (extension).doesExist (fileName)) { - std::string path = dataFileCollections.getCollection (extension).getPath (fileName).string(); - - std::ifstream stream (path.c_str()); + boost::filesystem::ifstream stream ( + dataFileCollections.getCollection (extension).getPath (fileName)); if (!stream.is_open()) throw std::runtime_error ("failed to open translation file: " + fileName); diff --git a/extern/oics/tinyxml.cpp b/extern/oics/tinyxml.cpp index 29a4768aa..21b2d9c9a 100644 --- a/extern/oics/tinyxml.cpp +++ b/extern/oics/tinyxml.cpp @@ -31,18 +31,34 @@ distribution. #include "tinyxml.h" +#ifdef _WIN32 +#include // import MultiByteToWideChar +#endif + bool TiXmlBase::condenseWhiteSpace = true; // Microsoft compiler security FILE* TiXmlFOpen( const char* filename, const char* mode ) { - #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + #if defined(_WIN32) FILE* fp = 0; - errno_t err = fopen_s( &fp, filename, mode ); - if ( !err && fp ) - return fp; - return 0; + size_t len = strlen(filename); + wchar_t *wname = new wchar_t[len + 1]; + memset(wname, 0, sizeof(*wname) * (len + 1)); + wchar_t wmode[32] = { 0 }; + + MultiByteToWideChar(CP_UTF8, 0, filename, len, wname, len); + MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, sizeof(wmode) / sizeof(*wmode)); + + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + errno_t err = _wfopen_s( &fp, wname, wmode ); + #else + fp = _wfopen(wname, wmode); + #endif + delete[] wname; + + return fp; #else return fopen( filename, mode ); #endif diff --git a/extern/shiny/Main/Preprocessor.cpp b/extern/shiny/Main/Preprocessor.cpp index 1a97668bc..c03879d46 100644 --- a/extern/shiny/Main/Preprocessor.cpp +++ b/extern/shiny/Main/Preprocessor.cpp @@ -4,6 +4,59 @@ #include #include +#include + +/* + Almost exact copy of load_file_to_string policy found in + boost::wave headers with the only change that it uses + boost::filesystem facility to handle UTF-8 paths used + throughout OpenMW (bfs::fstream, bfs::path). + + Original namespace is used due to required bost::wave + internal symbols. +*/ +namespace boost { +namespace wave { +namespace iteration_context_policies { + + struct load_utf8_path_to_string + { + template + class inner + { + public: + template + static void init_iterators(IterContextT &iter_ctx, + PositionT const &act_pos, language_support language) + { + typedef typename IterContextT::iterator_type iterator_type; + namespace bfs = boost::filesystem; + + // read in the file + bfs::ifstream instream(bfs::path(iter_ctx.filename.c_str())); + if (!instream.is_open()) { + BOOST_WAVE_THROW_CTX(iter_ctx.ctx, preprocess_exception, + bad_include_file, iter_ctx.filename.c_str(), act_pos); + return; + } + instream.unsetf(std::ios::skipws); + + iter_ctx.instring.assign( + std::istreambuf_iterator(instream.rdbuf()), + std::istreambuf_iterator()); + + iter_ctx.first = iterator_type( + iter_ctx.instring.begin(), iter_ctx.instring.end(), + PositionT(iter_ctx.filename), language); + iter_ctx.last = iterator_type(); + } + + private: + std::string instring; + }; + }; +} } } + namespace sh { std::string Preprocessor::preprocess (std::string source, const std::string& includePath, std::vector definitions, const std::string& name) @@ -29,7 +82,7 @@ namespace sh // match the iterator type used during construction of the context // instance (see below). It is the type of the underlying input stream. typedef boost::wave::context context_type; diff --git a/libs/openengine/gui/loglistener.cpp b/libs/openengine/gui/loglistener.cpp new file mode 100644 index 000000000..da36b90a2 --- /dev/null +++ b/libs/openengine/gui/loglistener.cpp @@ -0,0 +1,39 @@ +#include "loglistener.hpp" + +#include +#include + +#include + +namespace MyGUI +{ + void CustomLogListener::open() + { + mStream.open(boost::filesystem::path(mFileName), std::ios_base::out); + } + + void CustomLogListener::close() + { + if (mStream.is_open()) + mStream.close(); + } + + void CustomLogListener::flush() + { + if (mStream.is_open()) + mStream.flush(); + } + + void CustomLogListener::log(const std::string& _section, LogLevel _level, const struct tm* _time, const std::string& _message, const char* _file, int _line) + { + if (mStream.is_open()) + { + const char* separator = " | "; + mStream << std::setw(2) << std::setfill('0') << _time->tm_hour << ":" + << std::setw(2) << std::setfill('0') << _time->tm_min << ":" + << std::setw(2) << std::setfill('0') << _time->tm_sec << separator + << _section << separator << _level.print() << separator + << _message << separator << _file << separator << _line << std::endl; + } + } +} diff --git a/libs/openengine/gui/loglistener.hpp b/libs/openengine/gui/loglistener.hpp new file mode 100644 index 000000000..47978ba44 --- /dev/null +++ b/libs/openengine/gui/loglistener.hpp @@ -0,0 +1,37 @@ +#ifndef OPENENGINE_MYGUI_LOGLISTENER_H +#define OPENENGINE_MYGUI_LOGLISTENER_H + +#include +#include + +#include + +namespace MyGUI +{ + /// \brief Custom MyGUI::ILogListener interface implementation + /// being able to portably handle UTF-8 encoded path. + class CustomLogListener : public ILogListener + { + public: + CustomLogListener(const std::string &name) + : mFileName(name) + {} + + ~CustomLogListener() {} + + virtual void open(); + virtual void close(); + virtual void flush(); + + virtual void log(const std::string& _section, LogLevel _level, const struct tm* _time, const std::string& _message, const char* _file, int _line); + + const std::string& getFileName() const { return mFileName; } + + private: + boost::filesystem::ofstream mStream; + std::string mFileName; + }; + +} + +#endif diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 91937d24b..383d37640 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -1,9 +1,14 @@ #include "manager.hpp" +#include "loglistener.hpp" #include #include #include +#include +#include +#include + #include #include @@ -554,8 +559,34 @@ public: } }; -} +/// \brief Helper class holding data that required during +/// MyGUI log creation +class LogFacility +{ + ConsoleLogListener mConsole; + CustomLogListener mFile; + LevelLogFilter mFilter; + LogSource mSource; + +public: + + LogFacility(const std::string &output, bool console) + : mFile(output) + { + mConsole.setEnabled(console); + mFilter.setLoggingLevel(LogLevel::Info); + mSource.addLogListener(&mFile); + mSource.addLogListener(&mConsole); + mSource.setLogFilter(&mFilter); + + mSource.open(); + } + + LogSource *getSource() { return &mSource; } +}; + +} void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir) { @@ -586,10 +617,10 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool mRenderManager = new MyGUI::OgreRenderManager(); mDataManager = new MyGUI::FixedOgreDataManager(); - LogManager::getInstance().setSTDOutputEnabled(logging); - - if (!theLogFile.empty()) - LogManager::getInstance().createDefaultSource(theLogFile); + // Do not use default log since it don't support Unicode path on Windows. + // Instead, manually create log source using LogFacility and pass it. + mLogFacility = new MyGUI::LogFacility(theLogFile, logging); + LogManager::getInstance().addLogSource(mLogFacility->getSource()); if (mShaderRenderManager) mShaderRenderManager->initialise(wnd, mgr); @@ -648,5 +679,7 @@ void MyGUIManager::shutdown() delete mLogManager; mLogManager = NULL; } + delete mLogFacility; + mGui = NULL; } diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index cca70dfcf..8923a5cc8 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -10,6 +10,7 @@ namespace MyGUI class OgreDataManager; class OgreRenderManager; class ShaderBasedRenderManager; + class LogFacility; } namespace Ogre @@ -25,6 +26,7 @@ namespace GUI { MyGUI::Gui *mGui; MyGUI::LogManager* mLogManager; + MyGUI::LogFacility* mLogFacility; MyGUI::OgreDataManager* mDataManager; MyGUI::OgreRenderManager* mRenderManager; MyGUI::ShaderBasedRenderManager* mShaderRenderManager; diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index caf62546e..8fcf615ba 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -15,6 +15,9 @@ #include +#include +#include + #include #include @@ -48,7 +51,41 @@ void OgreRenderer::update(float dt) void OgreRenderer::screenshot(const std::string &file) { - mWindow->writeContentsToFile(file); + /* Since Ogre uses narrow character interfaces, it does not support + Unicode paths on Windows. Therefore we had to implement screenshot + saving manually. + */ + namespace bfs = boost::filesystem; + bfs::ofstream out(bfs::path(file), std::ios::binary); + + Ogre::Image image; + + Ogre::PixelFormat pf = mWindow->suggestPixelFormat(); + int w = mWindow->getWidth(); + int h = mWindow->getHeight(); + + image.loadDynamicImage( + OGRE_ALLOC_T(Ogre::uchar, w * h * Ogre::PixelUtil::getNumElemBytes(pf), Ogre::MEMCATEGORY_GENERAL), + w, h, 1, pf, true + ); + mWindow->copyContentsToMemory(image.getPixelBox()); + + Ogre::DataStreamPtr stream = image.encode("png"); + Ogre::MemoryDataStream *mem = dynamic_cast(stream.get()); + if (mem != 0) { // likely + const char *ptr = reinterpret_cast(mem->getCurrentPtr()); + out.write(ptr, mem->size()); + } + else { + char buf[4096]; + size_t size = stream->size(); + while (size > 0) { + size_t chunk = (size > sizeof(buf)) ? sizeof(buf) : size; + stream->read(buf, chunk); + out.write(buf, chunk); + size -= chunk; + } + } } float OgreRenderer::getFPS()