diff --git a/AUTHORS.md b/AUTHORS.md index b97642aba..60e969cdc 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -22,6 +22,7 @@ Programmers Alexander Nadeau (wareya) Alexander Olofsson (Ace) Allofich + AnyOldName3 Austin Salgat (Salgat) Artem Kotsynyak (greye) artemutin diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index a4bf1fe5b..f54905a2c 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -79,19 +79,19 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("data", bpo::value()->default_value(Files::PathContainer(), "data") ->multitoken()->composing(), "set data directories (later directories have higher priority)") - ("data-local", bpo::value()->default_value(""), + ("data-local", bpo::value()->default_value(""), "set local data directory (highest priority)") - ("fallback-archive", bpo::value()->default_value(StringsVector(), "fallback-archive") + ("fallback-archive", bpo::value()->default_value(Files::EscapeStringVector(), "fallback-archive") ->multitoken(), "set fallback BSA archives (later archives have higher priority)") - ("resources", bpo::value()->default_value("resources"), + ("resources", bpo::value()->default_value("resources"), "set resources directory") - ("start", bpo::value()->default_value(""), + ("start", bpo::value()->default_value(""), "set initial cell") - ("content", bpo::value()->default_value(StringsVector(), "") + ("content", bpo::value()->default_value(Files::EscapeStringVector(), "") ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") ("no-sound", bpo::value()->implicit_value(true) @@ -109,7 +109,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-console", bpo::value()->implicit_value(true) ->default_value(false), "enable console-only script functionality") - ("script-run", bpo::value()->default_value(""), + ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") ("script-warn", bpo::value()->implicit_value (1) @@ -119,13 +119,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat "\t1 - show warning but consider script as correctly compiled anyway\n" "\t2 - treat warnings as errors") - ("script-blacklist", bpo::value()->default_value(StringsVector(), "") + ("script-blacklist", bpo::value()->default_value(Files::EscapeStringVector(), "") ->multitoken(), "ignore the specified script (if the use of the blacklist is enabled)") ("script-blacklist-use", bpo::value()->implicit_value(true) ->default_value(true), "enable script blacklisting") - ("load-savegame", bpo::value()->default_value(""), + ("load-savegame", bpo::value()->default_value(""), "load a save game file on game startup (specify an absolute filename or a filename relative to the current working directory)") ("skip-menu", bpo::value()->implicit_value(true) @@ -137,14 +137,14 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") - ( "encoding", bpo::value()-> + ("encoding", bpo::value()-> default_value("win1252"), "Character encoding used in OpenMW game messages:\n" "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" "\n\twin1252 - Western European (Latin) alphabet, used by default") - ("fallback", bpo::value()->default_value(FallbackMap(), "") + ("fallback", bpo::value()->default_value(FallbackMap(), "") ->multitoken()->composing(), "fallback values") ("no-grab", "Don't grab mouse cursor") @@ -173,20 +173,20 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat { cfgMgr.readConfiguration(variables, desc, true); - Version::Version v = Version::getOpenmwVersion(variables["resources"].as()); + Version::Version v = Version::getOpenmwVersion(variables["resources"].as().toStdString()); std::cout << v.describe() << std::endl; return false; } cfgMgr.readConfiguration(variables, desc); - Version::Version v = Version::getOpenmwVersion(variables["resources"].as()); + Version::Version v = Version::getOpenmwVersion(variables["resources"].as().toStdString()); std::cout << v.describe() << std::endl; engine.setGrabMouse(!variables.count("no-grab")); // Font encoding settings - std::string encoding(variables["encoding"].as()); + std::string encoding(variables["encoding"].as().toStdString()); std::cout << ToUTF8::encodingUsingMessage(encoding) << std::endl; engine.setEncoding(ToUTF8::calculateEncoding(encoding)); @@ -195,7 +195,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat Files::PathContainer dataDirs(variables["data"].as()); - std::string local(variables["data-local"].as()); + std::string local(variables["data-local"].as().toStdString()); if (!local.empty()) { dataDirs.push_back(Files::PathContainer::value_type(local)); @@ -206,15 +206,15 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setDataDirs(dataDirs); // fallback archives - StringsVector archives = variables["fallback-archive"].as(); + StringsVector archives = variables["fallback-archive"].as().toStdStringVector(); for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); ++it) { engine.addArchive(*it); } - engine.setResourceDir(variables["resources"].as()); + engine.setResourceDir(variables["resources"].as().toStdString()); - StringsVector content = variables["content"].as(); + StringsVector content = variables["content"].as().toStdStringVector(); if (content.empty()) { std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl; @@ -229,7 +229,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat } // startup-settings - engine.setCell(variables["start"].as()); + engine.setCell(variables["start"].as().toStdString()); engine.setSkipMenu (variables["skip-menu"].as(), variables["new-game"].as()); if (!variables["skip-menu"].as() && variables["new-game"].as()) std::cerr << "new-game used without skip-menu -> ignoring it" << std::endl; @@ -239,11 +239,11 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setCompileAllDialogue(variables["script-all-dialogue"].as()); engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setScriptConsoleMode (variables["script-console"].as()); - engine.setStartupScript (variables["script-run"].as()); + engine.setStartupScript (variables["script-run"].as().toStdString()); engine.setWarningsMode (variables["script-warn"].as()); - engine.setScriptBlacklist (variables["script-blacklist"].as()); + engine.setScriptBlacklist (variables["script-blacklist"].as().toStdStringVector()); engine.setScriptBlacklistUse (variables["script-blacklist-use"].as()); - engine.setSaveGameFile (variables["load-savegame"].as()); + engine.setSaveGameFile (variables["load-savegame"].as().toStdString()); // other settings engine.setSoundUsage(!variables["no-sound"].as()); diff --git a/components/fallback/validate.hpp b/components/fallback/validate.hpp index 3b6398d6a..d82ef5770 100644 --- a/components/fallback/validate.hpp +++ b/components/fallback/validate.hpp @@ -3,6 +3,8 @@ #include +#include + // Parses and validates a fallback map from boost program_options. // Note: for boost to pick up the validate function, you need to pull in the namespace e.g. // by using namespace Fallback; @@ -11,38 +13,59 @@ namespace Fallback { struct FallbackMap { - std::map mMap; + std::map mMap; }; void validate(boost::any &v, std::vector const &tokens, FallbackMap*, int) { - if(v.empty()) + if (v.empty()) { v = boost::any(FallbackMap()); } FallbackMap *map = boost::any_cast(&v); - for(std::vector::const_iterator it=tokens.begin(); it != tokens.end(); ++it) + for (std::vector::const_iterator it = tokens.begin(); it != tokens.end(); ++it) { - int sep = it->find(","); - if(sep < 1 || sep == (int)it->length()-1) - #if (BOOST_VERSION < 104200) + std::string temp = Files::EscapeHashString::processString(*it); + int sep = temp.find(","); + if (sep < 1 || sep == (int)temp.length() - 1) +#if (BOOST_VERSION < 104200) throw boost::program_options::validation_error("invalid value"); - #else +#else throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value); - #endif +#endif - std::string key(it->substr(0,sep)); - std::string value(it->substr(sep+1)); + std::string key(temp.substr(0, sep)); + std::string value(temp.substr(sep + 1)); - if(map->mMap.find(key) == map->mMap.end()) + if (map->mMap.find(key) == map->mMap.end()) { - map->mMap.insert(std::make_pair (key,value)); + map->mMap.insert(std::make_pair(key, value)); } } } +} +namespace Files { + void validate(boost::any &v, const std::vector &tokens, Files::EscapeHashString * eHS, int a) + { + boost::program_options::validators::check_first_occurrence(v); + + if (v.empty()) + v = boost::any(EscapeHashString(boost::program_options::validators::get_single_string(tokens))); + } + + void validate(boost::any &v, const std::vector &tokens, EscapeStringVector *, int) + { + if (v.empty()) + v = boost::any(EscapeStringVector()); + + EscapeStringVector * eSV = boost::any_cast(&v); + + for (std::vector::const_iterator it = tokens.begin(); it != tokens.end(); ++it) + eSV->mVector.push_back(EscapeHashString(*it)); + } } #endif diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 5ac3dd695..3a7f57949 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -3,9 +3,11 @@ #include #include #include +#include #include #include +#include #include /** @@ -139,8 +141,11 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, if (!mSilent) std::cout << "Loading config file: " << cfgFile.string() << "... "; - boost::filesystem::ifstream configFileStream(cfgFile); - if (configFileStream.is_open()) + boost::filesystem::ifstream configFileStreamUnfiltered(cfgFile); + boost::iostreams::filtering_istream configFileStream; + configFileStream.push(escape_hash_filter()); + configFileStream.push(configFileStreamUnfiltered); + if (configFileStreamUnfiltered.is_open()) { boost::program_options::store(boost::program_options::parse_config_file( configFileStream, description, true), variables); @@ -159,6 +164,152 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, return false; } +const int escape_hash_filter::sEscape = '@'; +const int escape_hash_filter::sEscapeIdentifier = 'a'; +const int escape_hash_filter::sHashIdentifier = 'h'; + +escape_hash_filter::escape_hash_filter() : mNext(), mSeenNonWhitespace(false), mFinishLine(false) +{ +} + +escape_hash_filter::~escape_hash_filter() +{ +} + +template +int escape_hash_filter::get(Source & src) +{ + if (mNext.empty()) + { + int character = boost::iostreams::get(src); + bool record = true; + if (character == boost::iostreams::WOULD_BLOCK) + { + mNext.push(character); + record = false; + } + else if (character == EOF) + { + mSeenNonWhitespace = false; + mFinishLine = false; + mNext.push(character); + } + else if (character == '\n') + { + mSeenNonWhitespace = false; + mFinishLine = false; + mNext.push(character); + } + else if (mFinishLine) + { + mNext.push(character); + } + else if (character == '#') + { + if (mSeenNonWhitespace) + { + mNext.push(sEscape); + mNext.push(sHashIdentifier); + } + else + { + //it's fine being interpreted by Boost as a comment, and so is anything afterwards + mNext.push(character); + mFinishLine = true; + } + } + else if (mPrevious == sEscape) + { + mNext.push(sEscape); + mNext.push(sEscapeIdentifier); + } + else + { + mNext.push(character); + } + if (!mSeenNonWhitespace && !isspace(character)) + mSeenNonWhitespace = true; + if (record) + mPrevious = character; + } + int retval = mNext.front(); + mNext.pop(); + return retval; +} + +std::string EscapeHashString::processString(const std::string & str) +{ + std::string temp = boost::replace_all_copy(str, std::string() + (char)escape_hash_filter::sEscape + (char)escape_hash_filter::sHashIdentifier, "#"); + boost::replace_all(temp, std::string() + (char)escape_hash_filter::sEscape + (char)escape_hash_filter::sEscapeIdentifier, std::string((char) escape_hash_filter::sEscape, 1)); + return temp; +} + +EscapeHashString::EscapeHashString() : mData() +{ +} + +EscapeHashString::EscapeHashString(const std::string & str) : mData(EscapeHashString::processString(str)) +{ +} + +EscapeHashString::EscapeHashString(const std::string & str, size_t pos, size_t len) : mData(EscapeHashString::processString(str), pos, len) +{ +} + +EscapeHashString::EscapeHashString(const char * s) : mData(EscapeHashString::processString(std::string(s))) +{ +} + +EscapeHashString::EscapeHashString(const char * s, size_t n) : mData(EscapeHashString::processString(std::string(s)), 0, n) +{ +} + +EscapeHashString::EscapeHashString(size_t n, char c) : mData(n, c) +{ +} + +template +EscapeHashString::EscapeHashString(InputIterator first, InputIterator last) : mData(EscapeHashString::processString(std::string(first, last))) +{ +} + +std::string EscapeHashString::toStdString() const +{ + return std::string(mData); +} + +std::istream & operator>> (std::istream & is, EscapeHashString & eHS) +{ + std::string temp; + is >> temp; + eHS = EscapeHashString(temp); + return is; +} + +std::ostream & operator<< (std::ostream & os, const EscapeHashString & eHS) +{ + os << eHS.mData; + return os; +} + +EscapeStringVector::EscapeStringVector() : mVector() +{ +} + +EscapeStringVector::~EscapeStringVector() +{ +} + +std::vector EscapeStringVector::toStdStringVector() const +{ + std::vector temp = std::vector(); + for (std::vector::const_iterator it = mVector.begin(); it != mVector.end(); ++it) + { + temp.push_back(it->toStdString()); + } + return temp; +} + const boost::filesystem::path& ConfigurationManager::getGlobalPath() const { return mFixedPath.getGlobalConfigPath(); diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index c05dbbb45..3ce995983 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -2,8 +2,10 @@ #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #include +#include #include +#include #include #include @@ -63,6 +65,65 @@ struct ConfigurationManager bool mSilent; }; + +/** + * \struct escape_hash_filter + */ +struct escape_hash_filter : public boost::iostreams::input_filter +{ + static const int sEscape; + static const int sHashIdentifier; + static const int sEscapeIdentifier; + + escape_hash_filter(); + virtual ~escape_hash_filter(); + + template int get(Source & src); + + private: + std::queue mNext; + int mPrevious; + + bool mSeenNonWhitespace; + bool mFinishLine; +}; + +/** + * \class EscapeHashString + */ +class EscapeHashString +{ + private: + std::string mData; + public: + static std::string processString(const std::string & str); + + EscapeHashString(); + EscapeHashString(const std::string & str); + EscapeHashString(const std::string & str, size_t pos, size_t len = std::string::npos); + EscapeHashString(const char * s); + EscapeHashString(const char * s, size_t n); + EscapeHashString(size_t n, char c); + template + EscapeHashString(InputIterator first, InputIterator last); + + std::string toStdString() const; + + friend std::ostream & operator<< (std::ostream & os, const EscapeHashString & eHS); +}; + +std::istream & operator>> (std::istream & is, EscapeHashString & eHS); + +struct EscapeStringVector +{ + std::vector mVector; + + EscapeStringVector(); + virtual ~EscapeStringVector(); + + std::vector toStdStringVector() const; +}; + } /* namespace Cfg */ #endif /* COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP */