openmw/apps/openmw/main.cpp
AnyOldName3 1b83b08d80 Merge remote-tracking branch 'upstream/master' into detain-hash
Tests which no longer work are commented out.
Some of these don't work because they're effectively testing for the
presence of bugs in the old implementation.
Others don't work because we're no longer accidentally disabling the
boost::program_options feature where it generates an error if only part
of a token gets consumed.
These will be fixed by later commits.
2021-10-10 23:50:50 +01:00

243 lines
7.9 KiB
C++

#include <components/version/version.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/escape.hpp>
#include <components/fallback/fallback.hpp>
#include <components/fallback/validate.hpp>
#include <components/debug/debugging.hpp>
#include <components/misc/rng.hpp>
#include "engine.hpp"
#include "options.hpp"
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
// makes __argc and __argv available on windows
#include <cstdlib>
#endif
#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))
#include <unistd.h>
#endif
using namespace Fallback;
/**
* \brief Parses application command line and calls \ref Cfg::ConfigurationManager
* to parse configuration files.
*
* Results are directly written to \ref Engine class.
*
* \retval true - Everything goes OK
* \retval false - Error
*/
bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::ConfigurationManager& cfgMgr)
{
// Create a local alias for brevity
namespace bpo = boost::program_options;
typedef std::vector<std::string> StringsVector;
bpo::options_description desc = OpenMW::makeOptionsDescription();
bpo::variables_map variables;
Files::parseArgs(argc, argv, variables, desc);
bpo::notify(variables);
if (variables.count ("help"))
{
getRawStdout() << desc << std::endl;
return false;
}
if (variables.count ("version"))
{
cfgMgr.readConfiguration(variables, desc, true);
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<boost::filesystem::path>().string());
getRawStdout() << v.describe() << std::endl;
return false;
}
bpo::variables_map composingVariables = Files::separateComposingVariables(variables, desc);
cfgMgr.readConfiguration(variables, desc);
Files::mergeComposingVariables(variables, composingVariables, desc);
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<boost::filesystem::path>().string());
Log(Debug::Info) << v.describe();
engine.setGrabMouse(!variables["no-grab"].as<bool>());
// Font encoding settings
std::string encoding(variables["encoding"].as<std::string>());
Log(Debug::Info) << ToUTF8::encodingUsingMessage(encoding);
engine.setEncoding(ToUTF8::calculateEncoding(encoding));
// directory settings
engine.enableFSStrict(variables["fs-strict"].as<bool>());
Files::PathContainer dataDirs(variables["data"].as<Files::PathContainer>());
Files::PathContainer::value_type local(variables["data-local"].as<Files::PathContainer::value_type>());
if (!local.empty())
dataDirs.push_back(local);
cfgMgr.processPaths(dataDirs);
engine.setResourceDir(variables["resources"].as<boost::filesystem::path>());
engine.setDataDirs(dataDirs);
// fallback archives
StringsVector archives = variables["fallback-archive"].as<StringsVector>();
for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); ++it)
{
engine.addArchive(*it);
}
StringsVector content = variables["content"].as<StringsVector>();
if (content.empty())
{
Log(Debug::Error) << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting...";
return false;
}
std::set<std::string> contentDedupe;
for (const auto& contentFile : content)
{
if (!contentDedupe.insert(contentFile).second)
{
Log(Debug::Error) << "Content file specified more than once: " << contentFile << ". Aborting...";
return false;
}
}
for (auto& file : content)
{
engine.addContentFile(file);
}
StringsVector groundcover = variables["groundcover"].as<StringsVector>();
for (auto& file : groundcover)
{
engine.addGroundcoverFile(file);
}
StringsVector luaScriptLists = variables["lua-scripts"].as<StringsVector>();
for (const auto& file : luaScriptLists)
engine.addLuaScriptListFile(file);
// startup-settings
engine.setCell(variables["start"].as<std::string>());
engine.setSkipMenu (variables["skip-menu"].as<bool>(), variables["new-game"].as<bool>());
if (!variables["skip-menu"].as<bool>() && variables["new-game"].as<bool>())
Log(Debug::Warning) << "Warning: new-game used without skip-menu -> ignoring it";
// scripts
engine.setCompileAll(variables["script-all"].as<bool>());
engine.setCompileAllDialogue(variables["script-all-dialogue"].as<bool>());
engine.setScriptConsoleMode (variables["script-console"].as<bool>());
engine.setStartupScript (variables["script-run"].as<std::string>());
engine.setWarningsMode (variables["script-warn"].as<int>());
engine.setScriptBlacklist (variables["script-blacklist"].as<StringsVector>());
engine.setScriptBlacklistUse (variables["script-blacklist-use"].as<bool>());
engine.setSaveGameFile (variables["load-savegame"].as<boost::filesystem::path>().string());
// other settings
Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
engine.setSoundUsage(!variables["no-sound"].as<bool>());
engine.setActivationDistanceOverride (variables["activate-dist"].as<int>());
engine.enableFontExport(variables["export-fonts"].as<bool>());
engine.setRandomSeed(variables["random-seed"].as<unsigned int>());
return true;
}
namespace
{
class OSGLogHandler : public osg::NotifyHandler
{
void notify(osg::NotifySeverity severity, const char* msg) override
{
// Copy, because osg logging is not thread safe.
std::string msgCopy(msg);
if (msgCopy.empty())
return;
Debug::Level level;
switch (severity)
{
case osg::ALWAYS:
case osg::FATAL:
level = Debug::Error;
break;
case osg::WARN:
case osg::NOTICE:
level = Debug::Warning;
break;
case osg::INFO:
level = Debug::Info;
break;
case osg::DEBUG_INFO:
case osg::DEBUG_FP:
default:
level = Debug::Debug;
}
std::string_view s(msgCopy);
if (s.size() < 1024)
Log(level) << (s.back() == '\n' ? s.substr(0, s.size() - 1) : s);
else
{
while (!s.empty())
{
size_t lineSize = 1;
while (lineSize < s.size() && s[lineSize - 1] != '\n')
lineSize++;
Log(level) << s.substr(0, s[lineSize - 1] == '\n' ? lineSize - 1 : lineSize);
s = s.substr(lineSize);
}
}
}
};
}
int runApplication(int argc, char *argv[])
{
#ifdef __APPLE__
boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));
boost::filesystem::current_path(binary_path.parent_path());
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif
osg::setNotifyHandler(new OSGLogHandler());
Files::ConfigurationManager cfgMgr;
std::unique_ptr<OMW::Engine> engine;
engine.reset(new OMW::Engine(cfgMgr));
if (parseOptions(argc, argv, *engine, cfgMgr))
{
engine->go();
}
return 0;
}
#ifdef ANDROID
extern "C" int SDL_main(int argc, char**argv)
#else
int main(int argc, char**argv)
#endif
{
return wrapApplication(&runApplication, argc, argv, "OpenMW");
}
// Platform specific for Windows when there is no console built into the executable.
// Windows will call the WinMain function instead of main in this case, the normal
// main function is then called with the __argc and __argv parameters.
#if defined(_WIN32) && !defined(_CONSOLE)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
return main(__argc, __argv);
}
#endif