int main(int argc, char *argv[]) { Settings ini; Game game; string gameStr; // allow for autodetection override string bosslogFormat; fs::path sortfile; //modlist/masterlist to sort plugins using. /////////////////////////////// // Set up initial conditions /////////////////////////////// LOG_INFO("BOSS starting..."); LOG_INFO("Parsing Ini..."); //Parse ini file if found. Can't just use BOOST's program options ini parser because of the CSS syntax and spaces. if (fs::exists(ini_path)) { try { ini.Load(ini_path); } catch (boss_error &e) { LOG_ERROR("Error: %s", e.getString().c_str()); //Error will be added to log once format has been set. } } else { try { ini.Save(ini_path, AUTODETECT); } catch (boss_error &e) { ini.ErrorBuffer(ParsingError("Error: " + e.getString())); } } //Specify location of language dictionaries boost::locale::generator gen; gen.add_messages_path(l10n_path.string()); gen.add_messages_domain("messages"); //Set the locale to get encoding and language conversions working correctly. string localeId = ""; if (gl_language == ENGLISH) localeId = "en.UTF-8"; else if (gl_language == SPANISH) localeId = "es.UTF-8"; else if (gl_language == GERMAN) localeId = "de.UTF-8"; else if (gl_language == RUSSIAN) localeId = "ru.UTF-8"; else if (gl_language == SIMPCHINESE) localeId = "zh.UTF-8"; try { locale::global(gen(localeId)); cout.imbue(locale()); } catch(exception &e) { LOG_ERROR("could not implement translation: %s", e.what()); cout << e.what() << endl; } locale global_loc = locale(); locale loc(global_loc, new boost::filesystem::detail::utf8_codecvt_facet()); boost::filesystem::path::imbue(loc); ////////////////////////////// // Handle Command Line Args ////////////////////////////// // declare the supported options po::options_description opts("Options"); opts.add_options() ("help,h", translate("produces this help message").str().c_str()) ("version,V", translate("prints the version banner").str().c_str()) ("update,u", po::value(&gl_update)->zero_tokens(), translate("automatically update the local copy of the" " masterlist to the latest version" " available on the web before sorting").str().c_str()) ("no-update,U", translate("inhibit the automatic masterlist updater").str().c_str()) ("only-update,o", po::value(&gl_update_only)->zero_tokens(), translate("automatically update the local copy of the" " masterlist to the latest version" " available on the web but don't sort right" " now").str().c_str()) ("silent,s", po::value(&gl_silent)->zero_tokens(), translate("don't launch a browser to show the HTML log" " at program completion").str().c_str()) ("revert,r", po::value(&gl_revert)->implicit_value(1, ""), translate("revert to a previous load order. this" " parameter optionally accepts values of 1 or" " 2, indicating how many undo steps to apply." " if no option value is specified, it" " defaults to 1").str().c_str()) ("verbose,v", po::value(&gl_debug_verbosity)->implicit_value(1, ""), translate("specify verbosity level (0-3) of the debugging output. 0 is the" " default, showing only WARN and ERROR messges." " 1 (INFO and above) is implied if this option" " is specified without an argument. higher" " values increase the verbosity further").str().c_str()) ("game,g", po::value(&gameStr), translate("override game autodetection. valid values" " are: 'Oblivion', 'Nehrim', 'Fallout3'," " 'FalloutNV', and 'Skyrim'").str().c_str()) ("crc-display,c", po::value(&gl_show_CRCs)->zero_tokens(), translate("show mod file CRCs, so that a file's CRC can be" " added to the masterlist in a conditional").str().c_str()) ("format,f", po::value(&bosslogFormat), translate("select output format. valid values" " are: 'html', 'text'").str().c_str()) ("trial-run,t", po::value(&gl_trial_run)->zero_tokens(), translate("run BOSS without actually making any changes to load order").str().c_str()); // parse command line arguments po::variables_map vm; try{ po::store(po::command_line_parser(argc, argv).options(opts).run(), vm); po::notify(vm); }catch (po::multiple_occurrences &){ LOG_ERROR("cannot specify options multiple times; please use the '--help' option to see usage instructions"); Fail(); }catch (exception & e){ LOG_ERROR("%s; please use the '--help' option to see usage instructions", e.what()); Fail(); } // set alternative output stream for logger and whether to track log statement origins if (gl_debug_verbosity > 0) g_logger.setStream(debug_log_path.string().c_str()); if (gl_debug_verbosity < 0) { LOG_ERROR("invalid option for 'verbose' parameter: %d", gl_debug_verbosity); Fail(); } g_logger.setVerbosity(static_cast<LogVerbosity>(LV_WARN + gl_debug_verbosity)); // it's ok if this number is too high. setVerbosity will handle it if (vm.count("help")) { ShowUsage(opts); exit(0); } if (vm.count("version")) { ShowVersion(); exit(0); } if (vm.count("no-update")) { gl_update = false; } if ((vm.count("update")) && (vm.count("no-update"))) { LOG_ERROR("invalid options: --update,-u and --no-update,-U cannot both be given."); Fail(); } if (vm.count("revert") && (gl_revert < 1 || gl_revert > 2)) { LOG_ERROR("invalid option for 'revert' parameter: %d", gl_revert); Fail(); } if (vm.count("game")) { // sanity check and parse argument if (boost::iequals("Oblivion", gameStr)) gl_game = OBLIVION; else if (boost::iequals("Fallout3", gameStr)) gl_game = FALLOUT3; else if (boost::iequals("Nehrim", gameStr)) gl_game = NEHRIM; else if (boost::iequals("FalloutNV", gameStr)) gl_game = FALLOUTNV; else if (boost::iequals("Skyrim", gameStr)) gl_game = SKYRIM; else { LOG_ERROR("invalid option for 'game' parameter: '%s'", gameStr.c_str()); Fail(); } LOG_DEBUG("game ini setting overridden with: '%s' (%d)", gameStr.c_str(), gl_game); } if (vm.count("format")) { // sanity check and parse argument string bosslogFormat = vm["format"].as<string>(); if (bosslogFormat == "html") gl_log_format = HTML; else if (bosslogFormat == "text") gl_log_format = PLAINTEXT; else { LOG_ERROR("invalid option for 'format' parameter: '%s'", bosslogFormat.c_str()); Fail(); } LOG_DEBUG("BOSSlog format set to: '%s'", bosslogFormat.c_str()); } ///////////////////////////////////////// // Check for critical error conditions ///////////////////////////////////////// //Game checks. LOG_DEBUG("Detecting game..."); try { gl_last_game = AUTODETECT; //Clear this setting in case the GUI was run. vector<uint32_t> detected, undetected; uint32_t detectedGame = DetectGame(detected, undetected); if (detectedGame == AUTODETECT) { //Now check what games were found. if (detected.empty()) throw boss_error(BOSS_ERROR_NO_GAME_DETECTED); else if (detected.size() == 1) detectedGame = detected.front(); else { size_t ans; //Ask user to choose game. cout << endl << translate("Please pick which game to run BOSS for:") << endl; for (size_t i=0; i < detected.size(); i++) cout << i << " : " << Game(detected[i], "", true).Name() << endl; cin >> ans; if (ans < 0 || ans >= detected.size()) { cout << translate("Invalid selection.") << endl; throw boss_error(BOSS_ERROR_NO_GAME_DETECTED); } detectedGame = detected[ans]; } } game = Game(detectedGame); game.CreateBOSSGameFolder(); LOG_INFO("Game detected: %s", game.Name().c_str()); } catch (boss_error &e) { LOG_ERROR("Critical Error: %s", e.getString().c_str()); game.bosslog.SetFormat(gl_log_format); game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << translate("Critical Error: ") << e.getString() << LINE_BREAK << translate("Check the Troubleshooting section of the ReadMe for more information and possible solutions.") << LINE_BREAK << translate("Utility will end now."); try { game.bosslog.Save(game.Log(gl_log_format), true); } catch (boss_error &e) { LOG_ERROR("Critical Error: %s", e.getString().c_str()); } if ( !gl_silent ) Launch(game.Log(gl_log_format).string()); //Displays the BOSSlog.txt. exit (1); //fail in screaming heap. } game.bosslog.SetFormat(gl_log_format); game.bosslog.parsingErrors.push_back(ini.ErrorBuffer()); ///////////////////////////////////////////////////////// // Update masterlist ///////////////////////////////////////////////////////// if (gl_revert < 1 && (gl_update || gl_update_only)) { cout << endl << translate("Updating to the latest masterlist from the online repository...") << endl; LOG_DEBUG("Updating masterlist..."); try { string revision = UpdateMasterlist(game, progress, NULL); string message = (boost::format(translate("Masterlist updated; at revision: %1%.")) % revision).str(); game.bosslog.updaterOutput << LIST_ITEM_CLASS_SUCCESS << message; cout << endl << message << endl; } catch (boss_error &e) { game.bosslog.updaterOutput << LIST_ITEM_CLASS_ERROR << translate("Error: masterlist update failed.") << LINE_BREAK << (boost::format(translate("Details: %1%")) % e.getString()).str() << LINE_BREAK; LOG_ERROR("Error: masterlist update failed. Details: %s", e.getString().c_str()); } } else { string revision = GetMasterlistVersion(game); string message = (boost::format(translate("Masterlist updating disabled; at revision: %1%.")) % revision).str(); game.bosslog.updaterOutput << LIST_ITEM_CLASS_SUCCESS << message; } //If true, exit BOSS now. Flush earlyBOSSlogBuffer to the bosslog and exit. if (gl_update_only) { try { game.bosslog.Save(game.Log(gl_log_format), true); } catch (boss_error &e) { LOG_ERROR("Critical Error: %s", e.getString().c_str()); } if ( !gl_silent ) Launch(game.Log(gl_log_format).string()); //Displays the BOSSlog. return (0); } /////////////////////////////////// // Resume Error Condition Checks /////////////////////////////////// cout << endl << translate("BOSS working...") << endl; //Build and save modlist. try { game.modlist.Load(game, game.DataFolder()); if (gl_revert < 1) game.modlist.Save(game.Modlist(), game.OldModlist()); } catch (boss_error &e) { LOG_ERROR("Failed to load/save modlist, error was: %s", e.getString().c_str()); game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << (boost::format(translate("Critical Error: %1%")) % e.getString()).str() << LINE_BREAK << translate("Check the Troubleshooting section of the ReadMe for more information and possible solutions.") << LINE_BREAK << translate("Utility will end now."); try { game.bosslog.Save(game.Log(gl_log_format), true); } catch (boss_error &e) { LOG_ERROR("Critical Error: %s", e.getString().c_str()); } if ( !gl_silent ) Launch(game.Log(gl_log_format).string()); //Displays the BOSSlog.txt. exit (1); //fail in screaming heap. } ///////////////////////////////// // Parse Master- and Userlists ///////////////////////////////// //masterlist parse errors are critical, ini and userlist parse errors are not. //Set masterlist path to be used. if (gl_revert == 1) sortfile = game.Modlist(); else if (gl_revert == 2) sortfile = game.OldModlist(); else sortfile = game.Masterlist(); LOG_INFO("Using sorting file: %s", sortfile.string().c_str()); //Parse masterlist/modlist backup into data structure. try { LOG_INFO("Starting to parse sorting file: %s", sortfile.string().c_str()); game.masterlist.Load(game, sortfile); LOG_INFO("Starting to parse conditionals from sorting file: %s", sortfile.string().c_str()); game.masterlist.EvalConditions(game); game.masterlist.EvalRegex(game); game.bosslog.globalMessages = game.masterlist.GlobalMessageBuffer(); game.bosslog.parsingErrors.push_back(game.masterlist.ErrorBuffer()); } catch (boss_error &e) { LOG_ERROR("Critical Error: %s", e.getString().c_str()); if (e.getCode() == BOSS_ERROR_FILE_PARSE_FAIL) game.bosslog.criticalError << game.masterlist.ErrorBuffer(); else if (e.getCode() == BOSS_ERROR_CONDITION_EVAL_FAIL) game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << e.getString(); else game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << (boost::format(translate("Critical Error: %1%")) % e.getString()).str() << LINE_BREAK << translate("Check the Troubleshooting section of the ReadMe for more information and possible solutions.") << LINE_BREAK << translate("Utility will end now."); try { game.bosslog.Save(game.Log(gl_log_format), true); } catch (boss_error &e) { LOG_ERROR("Critical Error: %s", e.getString().c_str()); } if ( !gl_silent ) Launch(game.Log(gl_log_format).string()); //Displays the BOSSlog.txt. exit (1); //fail in screaming heap. } LOG_INFO("Starting to parse userlist."); try { game.userlist.Load(game, game.Userlist()); vector<ParsingError> errs = game.userlist.ErrorBuffer(); game.bosslog.parsingErrors.insert(game.bosslog.parsingErrors.end(), errs.begin(), errs.end()); } catch (boss_error &e) { vector<ParsingError> errs = game.userlist.ErrorBuffer(); game.bosslog.parsingErrors.insert(game.bosslog.parsingErrors.end(), errs.begin(), errs.end()); game.userlist.Clear(); //If userlist has parsing errors, empty it so no rules are applied. LOG_ERROR("Error: %s", e.getString().c_str()); } ////////////////////////////////// // Perform sorting functionality ////////////////////////////////// try { game.ApplyMasterlist(); LOG_INFO("masterlist now filled with ordered mods and modlist filled with unknowns."); game.ApplyUserlist(); LOG_INFO("userlist sorting process finished."); game.ScanSEPlugins(); game.SortPlugins(); game.bosslog.Save(game.Log(gl_log_format), true); } catch (boss_error &e) { LOG_ERROR("Critical Error: %s", e.getString().c_str()); game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << (boost::format(translate("Critical Error: %1%")) % e.getString()).str() << LINE_BREAK << translate("Check the Troubleshooting section of the ReadMe for more information and possible solutions.") << LINE_BREAK << translate("Utility will end now."); try { game.bosslog.Save(game.Log(gl_log_format), true); } catch (boss_error &e) { LOG_ERROR("Critical Error: %s", e.getString().c_str()); } if ( !gl_silent ) Launch(game.Log(gl_log_format).string()); //Displays the BOSSlog.txt. exit (1); //fail in screaming heap. } LOG_INFO("Launching boss log in browser."); if ( !gl_silent ) Launch(game.Log(gl_log_format).string()); //Displays the BOSSlog.txt. LOG_INFO("BOSS finished."); return (0); }