Files
libloot/src/api/api.cpp
T

686 lines
25 KiB
C++
Raw Permalink Normal View History

2014-03-06 19:52:46 +00:00
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
2014-02-02 11:04:00 +00:00
Copyright (C) 2013-2014 WrinklyNinja
2014-03-06 19:52:46 +00:00
This file is part of LOOT.
2014-03-06 19:52:46 +00:00
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
2014-03-06 19:52:46 +00:00
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
2014-03-06 19:52:46 +00:00
along with LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
#include "api.h"
#include "../backend/game.h"
#include "../backend/metadata.h"
#include "../backend/parsers.h"
#include "../backend/generators.h"
#include "../backend/error.h"
2013-07-22 10:38:30 +01:00
#include "../backend/streams.h"
#include <yaml-cpp/yaml.h>
#include <algorithm>
#include <clocale>
#include <list>
#include <vector>
2014-06-21 21:06:28 +01:00
#include <regex>
#include <unordered_set>
#include <unordered_map>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
#include <boost/filesystem.hpp>
2014-03-06 20:18:49 +00:00
const unsigned int loot_ok = loot::error::ok;
const unsigned int loot_error_liblo_error = loot::error::liblo_error;
const unsigned int loot_error_file_write_fail = loot::error::path_write_fail;
const unsigned int loot_error_parse_fail = loot::error::path_read_fail;
const unsigned int loot_error_condition_eval_fail = loot::error::condition_eval_fail;
const unsigned int loot_error_regex_eval_fail = loot::error::regex_eval_fail;
const unsigned int loot_error_no_mem = loot::error::no_mem;
const unsigned int loot_error_invalid_args = loot::error::invalid_args;
const unsigned int loot_error_no_tag_map = loot::error::no_tag_map;
const unsigned int loot_error_path_not_found = loot::error::path_not_found;
const unsigned int loot_error_no_game_detected = loot::error::no_game_detected;
const unsigned int loot_error_windows_error = loot::error::windows_error;
const unsigned int loot_error_sorting_error = loot::error::sorting_error;
const unsigned int loot_return_max = loot_error_sorting_error;
// The following are the games identifiers used by the API.
2014-03-07 19:24:02 +00:00
const unsigned int loot_game_tes4 = loot::Game::tes4;
const unsigned int loot_game_tes5 = loot::Game::tes5;
const unsigned int loot_game_fo3 = loot::Game::fo3;
const unsigned int loot_game_fonv = loot::Game::fonv;
2014-03-06 19:52:46 +00:00
// LOOT message types.
2014-03-07 19:24:02 +00:00
const unsigned int loot_message_say = loot::Message::say;
const unsigned int loot_message_warn = loot::Message::warn;
const unsigned int loot_message_error = loot::Message::error;
const unsigned int loot_message_tag = loot::Message::tag;
2014-03-06 19:52:46 +00:00
// LOOT message languages.
2014-03-07 19:24:02 +00:00
const unsigned int loot_lang_any = loot::Language::any;
const unsigned int loot_lang_english = loot::Language::english;
const unsigned int loot_lang_spanish = loot::Language::spanish;
const unsigned int loot_lang_russian = loot::Language::russian;
const unsigned int loot_lang_french = loot::Language::french;
const unsigned int loot_lang_chinese = loot::Language::chinese;
const unsigned int loot_lang_polish = loot::Language::polish;
const unsigned int loot_lang_brazilian_portuguese = loot::Language::brazilian_portuguese;
2014-06-05 17:22:36 +03:00
const unsigned int loot_lang_finnish = loot::Language::finnish;
2014-07-05 18:24:46 +02:00
const unsigned int loot_lang_german = loot::Language::german;
2014-01-12 11:37:11 +00:00
2014-03-06 19:52:46 +00:00
// LOOT cleanliness codes.
2014-03-06 20:18:49 +00:00
const unsigned int loot_needs_cleaning_no = 0;
const unsigned int loot_needs_cleaning_yes = 1;
const unsigned int loot_needs_cleaning_unknown = 2;
2014-03-06 20:18:49 +00:00
struct _loot_db_int {
_loot_db_int()
2014-06-21 23:45:40 +01:00
: extTagMap(nullptr),
extAddedTagIds(nullptr),
extRemovedTagIds(nullptr),
extMessageArray(nullptr),
extMessageArraySize(0) {
2014-03-06 20:18:49 +00:00
extMessage.type = loot_message_say;
2014-06-21 23:45:40 +01:00
extMessage.message = nullptr;
}
2014-03-06 20:18:49 +00:00
~_loot_db_int() {
delete [] extAddedTagIds;
delete [] extRemovedTagIds;
delete [] extMessage.message;
2014-06-21 23:45:40 +01:00
if (extTagMap != nullptr) {
for (size_t i=0; i < bashTagMap.size(); i++)
delete [] extTagMap[i]; //Gotta clear those allocated strings.
delete [] extTagMap;
}
2014-06-21 23:45:40 +01:00
if (extMessageArray != nullptr) {
for (size_t i=0; i < extMessageArraySize; i++)
delete [] extMessageArray[i].message; //Gotta clear those allocated strings.
delete [] extMessageArray;
}
}
2014-03-06 20:18:49 +00:00
loot::Game game;
std::list<loot::Plugin> metadata, rawMetadata, userMetadata, rawUserMetadata;
2014-06-21 23:33:44 +01:00
std::unordered_map<std::string, unsigned int> bashTagMap;
char ** extTagMap;
unsigned int * extAddedTagIds;
unsigned int * extRemovedTagIds;
2014-03-06 20:18:49 +00:00
loot_message extMessage;
loot_message * extMessageArray;
size_t extMessageArraySize;
};
2014-06-21 23:45:40 +01:00
char * extMessageStr = nullptr;
// std::string to null-terminated char string converter.
char * ToNewCString(std::string str) {
char * p = new char[str.length() + 1];
return strcpy(p, str.c_str());
}
2014-03-06 20:18:49 +00:00
unsigned int c_error(const loot::error& e) {
2014-02-02 15:37:46 +00:00
delete[] extMessageStr;
try {
extMessageStr = new char[strlen(e.what()) + 1];
strcpy(extMessageStr, e.what());
}
2014-05-10 16:41:08 +01:00
catch (std::bad_alloc& /*e*/) {
2014-06-21 23:45:40 +01:00
extMessageStr = nullptr;
2014-02-02 15:37:46 +00:00
}
return e.code();
}
unsigned int c_error(const unsigned int code, const std::string& what) {
2014-03-06 20:18:49 +00:00
return c_error(loot::error(code, what.c_str()));
2014-02-02 15:37:46 +00:00
}
2013-07-31 21:57:27 +01:00
//////////////////////////////
// Error Handling Functions
//////////////////////////////
// Outputs a string giving the details of the last time an error or
// warning return code was returned by a function. The string exists
// until this function is called again or until CleanUpAPI is called.
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_get_error_message (const char ** const message) {
2014-06-21 23:45:40 +01:00
if (message == nullptr)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null message pointer passed.");
*message = extMessageStr;
2014-03-06 20:18:49 +00:00
return loot_ok;
}
// Frees memory allocated to error string.
2014-03-06 20:18:49 +00:00
LOOT_API void loot_cleanup () {
delete [] extMessageStr;
2014-06-21 23:45:40 +01:00
extMessageStr = nullptr;
}
//////////////////////////////
// Version Functions
//////////////////////////////
2014-03-06 19:52:46 +00:00
// Returns whether this version of LOOT supports the API from the given
// LOOT version. Abstracts LOOT API stability policy away from clients.
2014-03-06 20:18:49 +00:00
LOOT_API bool loot_is_compatible (const unsigned int versionMajor, const unsigned int versionMinor, const unsigned int versionPatch) {
return versionMajor == loot::g_version_major && versionMinor == loot::g_version_minor;
}
2014-03-06 19:52:46 +00:00
// Returns the version string for this version of LOOT.
// The string exists until this function is called again or until
// CleanUpAPI is called.
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_get_version (unsigned int * const versionMajor, unsigned int * const versionMinor, unsigned int * const versionPatch) {
2014-06-21 23:45:40 +01:00
if (versionMajor == nullptr || versionMinor == nullptr || versionPatch == nullptr)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null pointer passed.");
2014-03-06 20:18:49 +00:00
*versionMajor = loot::g_version_major;
*versionMinor = loot::g_version_minor;
*versionPatch = loot::g_version_patch;
2014-03-06 20:18:49 +00:00
return loot_ok;
}
////////////////////////////////////
// Lifecycle Management Functions
////////////////////////////////////
// Explicitly manage database lifetime. Allows clients to free memory when
// they want/need to. clientGame sets the game the DB is for, and dataPath
// is the path to that game's Data folder, and is case-sensitive if the
// underlying filesystem is case-sensitive. This function also checks that
// plugins.txt and loadorder.txt (if they both exist) are in sync. If
2014-06-21 23:45:40 +01:00
// dataPath == nullptr then the API will attempt to detect the data path of
// the specified game.
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_create_db (loot_db * const db, const unsigned int clientGame, const char * const gamePath) {
2014-06-21 23:45:40 +01:00
if (db == nullptr || (clientGame != loot_game_tes4 && clientGame != loot_game_tes5 && clientGame != loot_game_fo3 && clientGame != loot_game_fonv))
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null pointer passed.");
//Set the locale to get encoding conversions working correctly.
std::setlocale(LC_CTYPE, "");
std::locale global_loc = std::locale();
std::locale loc(global_loc, new boost::filesystem::detail::utf8_codecvt_facet());
boost::filesystem::path::imbue(loc);
2014-01-12 11:37:11 +00:00
//Disable logging or else stdout will get overrun.
boost::log::core::get()->set_logging_enabled(false);
std::string game_path = "";
2014-06-21 23:45:40 +01:00
if (gamePath != nullptr)
game_path = gamePath;
2014-03-06 20:18:49 +00:00
loot::Game game;
try {
2014-03-06 20:18:49 +00:00
game = loot::Game(clientGame).SetPath(game_path).Init(); //This also checks to see if the game is installed if game_path is empty and throws an exception if it is not detected. It also creates a folder in %LOCALAPPDATA% and reads the active plugins list, but that shouldn't be an issue.
} catch (loot::error& e) {
2014-02-02 15:37:46 +00:00
return c_error(e);
}
2014-03-06 20:18:49 +00:00
loot_db retVal;
try {
2014-03-06 20:18:49 +00:00
retVal = new _loot_db_int;
} catch (std::bad_alloc& e) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_no_mem, e.what());
}
retVal->game = game;
*db = retVal;
2014-03-06 20:18:49 +00:00
return loot_ok;
}
// Destroys the given DB, freeing any memory allocated as part of its use.
2014-03-06 20:18:49 +00:00
LOOT_API void loot_destroy_db (loot_db db) {
delete db;
}
///////////////////////////////////
// Database Loading Functions
///////////////////////////////////
// Loads the masterlist and userlist from the paths specified.
// Can be called multiple times. On error, the database is unchanged.
// Paths are case-sensitive if the underlying filesystem is case-sensitive.
// masterlistPath and userlistPath are files.
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_load_lists (loot_db db, const char * const masterlistPath,
const char * const userlistPath) {
2014-06-21 23:45:40 +01:00
if (db == nullptr || masterlistPath == nullptr)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null pointer passed.");
2014-03-06 20:18:49 +00:00
std::list<loot::Plugin> temp;
std::list<loot::Plugin> userTemp;
2013-07-27 08:04:34 +01:00
try {
if (boost::filesystem::exists(masterlistPath)) {
loot::ifstream in(masterlistPath);
YAML::Node tempNode = YAML::Load(in);
in.close();
temp = tempNode["plugins"].as< std::list<loot::Plugin> >();
2013-07-27 08:04:34 +01:00
}
2014-02-02 15:37:46 +00:00
} catch (std::exception& e) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_parse_fail, e.what());
2013-07-27 08:04:34 +01:00
}
try {
2014-06-21 23:45:40 +01:00
if (userlistPath != nullptr) {
if (boost::filesystem::exists(userlistPath)) {
if (boost::algorithm::iends_with(userlistPath, ".yaml")) {
2014-03-06 20:18:49 +00:00
loot::ifstream in(userlistPath);
2014-01-29 15:25:15 +00:00
YAML::Node tempNode = YAML::Load(in);
in.close();
2014-03-06 20:18:49 +00:00
userTemp = tempNode["plugins"].as< std::list<loot::Plugin> >();
}
2014-01-13 02:34:51 +00:00
}
2014-01-12 11:37:11 +00:00
}
} catch (YAML::Exception& e) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_parse_fail, e.what());
}
//Also free memory.
db->bashTagMap.clear();
delete [] db->extAddedTagIds;
delete [] db->extRemovedTagIds;
2014-06-21 23:45:40 +01:00
if (db->extTagMap != nullptr) {
for (size_t i=0; i < db->bashTagMap.size(); i++)
delete [] db->extTagMap[i]; //Gotta clear those allocated strings.
delete [] db->extTagMap;
}
2014-06-21 23:45:40 +01:00
if (db->extMessageArray != nullptr) {
for (size_t i=0; i < db->extMessageArraySize; i++)
delete [] db->extMessageArray[i].message; //Gotta clear those allocated strings.
delete [] db->extMessageArray;
}
2014-06-21 23:45:40 +01:00
db->extAddedTagIds = nullptr;
db->extRemovedTagIds = nullptr;
db->extTagMap = nullptr;
db->extMessageArray = nullptr;
2014-02-02 15:37:46 +00:00
db->rawMetadata = temp;
db->metadata = temp;
db->userMetadata = userTemp;
db->rawUserMetadata = userTemp;
2014-03-06 20:18:49 +00:00
return loot_ok;
}
// Evaluates all conditional lines and regex mods the loaded masterlist.
// This exists so that Load() doesn't need to be called whenever the mods
// installed are changed. Evaluation does not take place unless this function
// is called. Repeated calls re-evaluate the masterlist from scratch each time,
// ignoring the results of any previous evaluations. Paths are case-sensitive
// if the underlying filesystem is case-sensitive.
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_eval_lists (loot_db db, const unsigned int language) {
2014-06-21 23:45:40 +01:00
if (db == nullptr)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null pointer passed.");
2014-03-06 20:18:49 +00:00
std::list<loot::Plugin> temp = db->rawMetadata;
try {
db->game.RefreshActivePluginsList();
2014-06-21 23:33:44 +01:00
for (auto it=temp.begin(); it != temp.end();) {
it->EvalAllConditions(db->game, language);
if (it->IsRegexPlugin()) {
boost::regex regex;
try {
regex = boost::regex(it->Name(), boost::regex::perl|boost::regex::icase);
} catch (boost::regex_error& e) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_regex_eval_fail, e.what());
}
for (boost::filesystem::directory_iterator itr(db->game.DataPath()); itr != boost::filesystem::directory_iterator(); ++itr) {
const std::string filename = itr->path().filename().string();
if (boost::regex_match(filename, regex)) {
2014-03-06 20:18:49 +00:00
loot::Plugin p = *it;
p.Name(filename);
temp.push_back(p);
}
}
2014-01-27 18:40:36 +00:00
it = temp.erase(it);
} else {
++it;
}
}
2014-03-06 20:18:49 +00:00
} catch (loot::error& e) {
2014-02-02 15:37:46 +00:00
return c_error(e);
}
db->metadata = temp;
temp = db->rawUserMetadata;
try {
2014-06-21 23:33:44 +01:00
for (auto it=temp.begin(); it != temp.end();) {
it->EvalAllConditions(db->game, language);
if (it->IsRegexPlugin()) {
boost::regex regex;
try {
regex = boost::regex(it->Name(), boost::regex::perl|boost::regex::icase);
} catch (boost::regex_error& e) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_regex_eval_fail, e.what());
}
for (boost::filesystem::directory_iterator itr(db->game.DataPath()); itr != boost::filesystem::directory_iterator(); ++itr) {
const std::string filename = itr->path().filename().string();
if (boost::regex_match(filename, regex)) {
2014-03-06 20:18:49 +00:00
loot::Plugin p = *it;
p.Name(filename);
temp.push_back(p);
}
}
2014-01-27 18:40:36 +00:00
it = temp.erase(it);
} else {
++it;
}
}
2014-03-06 20:18:49 +00:00
} catch (loot::error& e) {
2014-02-02 15:37:46 +00:00
return c_error(e);
}
db->userMetadata = temp;
2014-03-06 20:18:49 +00:00
return loot_ok;
}
//////////////////////////
// DB Access Functions
//////////////////////////
// Returns an array of the Bash Tags encounterred when loading the masterlist
// and userlist, and the number of tags in the returned array. The array and
// its contents are static and should not be freed by the client.
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_get_tag_map (loot_db db, char *** const tagMap, size_t * const numTags) {
2014-06-21 23:45:40 +01:00
if (db == nullptr || tagMap == nullptr || numTags == nullptr)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null pointer passed.");
//Clear existing array allocation.
2014-06-21 23:45:40 +01:00
if (db->extTagMap != nullptr) {
for (size_t i=0, max=db->bashTagMap.size(); i < max; ++i) {
delete [] db->extTagMap[i];
}
delete [] db->extTagMap;
2014-06-21 23:45:40 +01:00
db->extTagMap = nullptr;
}
//Initialise output.
2014-06-21 23:45:40 +01:00
*tagMap = nullptr;
*numTags = 0;
2014-06-21 23:33:44 +01:00
std::unordered_set<std::string> allTags;
2014-06-21 23:33:44 +01:00
for (const auto &plugin: db->metadata) {
std::set<loot::Tag> tags(plugin.Tags());
for (const auto &tag: tags) {
allTags.insert(tag.Name());
}
}
2014-06-21 23:33:44 +01:00
for (const auto &plugin : db->userMetadata) {
std::set<loot::Tag> tags(plugin.Tags());
for (const auto &tag : tags) {
allTags.insert(tag.Name());
}
}
if (allTags.empty())
2014-03-06 20:18:49 +00:00
return loot_ok;
try {
db->extTagMap = new char*[allTags.size()];
} catch (std::bad_alloc& e) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_no_mem, e.what());
}
unsigned int UID = 0;
try {
2014-06-21 23:33:44 +01:00
for (const auto &tag: allTags) {
db->bashTagMap.emplace(tag, UID);
//Also allocate memory.
2014-06-21 23:33:44 +01:00
db->extTagMap[UID] = ToNewCString(tag);
UID++;
}
} catch (std::bad_alloc& e) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_no_mem, e.what());
}
*tagMap = db->extTagMap;
*numTags = allTags.size();
2014-03-06 20:18:49 +00:00
return loot_ok;
}
// Returns arrays of Bash Tag UIDs for Bash Tags suggested for addition and removal
2014-03-06 19:52:46 +00:00
// by LOOT's masterlist and userlist, and the number of tags in each array.
// The returned arrays are valid until the db is destroyed or until the Load
// function is called. The arrays should not be freed by the client. modName is
// case-insensitive. If no Tags are found for an array, the array pointer (*tagIds)
2014-06-21 23:45:40 +01:00
// will be nullptr. The userlistModified bool is true if the userlist contains Bash Tag
// suggestion message additions.
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_get_plugin_tags (loot_db db, const char * const plugin,
unsigned int ** const tagIds_added,
size_t * const numTags_added,
unsigned int ** const tagIds_removed,
size_t * const numTags_removed,
bool * const userlistModified) {
2014-06-21 23:45:40 +01:00
if (db == nullptr || plugin == nullptr || tagIds_added == nullptr || numTags_added == nullptr || tagIds_removed == nullptr || numTags_removed == nullptr || userlistModified == nullptr)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null pointer passed.");
//Clear existing array allocations.
delete [] db->extAddedTagIds;
delete [] db->extRemovedTagIds;
2014-06-21 23:45:40 +01:00
db->extAddedTagIds = nullptr;
db->extRemovedTagIds = nullptr;
//Initialise output.
2014-06-21 23:45:40 +01:00
*tagIds_added = nullptr;
*tagIds_removed = nullptr;
*userlistModified = false;
*numTags_added = 0;
*numTags_removed = 0;
2014-06-21 23:33:44 +01:00
std::unordered_set<std::string> tagsAdded, tagsRemoved;
2014-03-06 20:18:49 +00:00
std::list<loot::Plugin>::iterator pluginIt = std::find(db->metadata.begin(), db->metadata.end(), loot::Plugin(plugin));
2013-01-12 23:30:57 +00:00
if (pluginIt != db->metadata.end()) {
2014-06-21 23:33:44 +01:00
std::set<loot::Tag> tags(pluginIt->Tags());
for (const auto &tag: tags) {
if (tag.IsAddition())
tagsAdded.insert(tag.Name());
else
2014-06-21 23:33:44 +01:00
tagsRemoved.insert(tag.Name());
}
}
2014-03-06 20:18:49 +00:00
pluginIt = std::find(db->userMetadata.begin(), db->userMetadata.end(), loot::Plugin(plugin));
2013-01-12 23:30:57 +00:00
if (pluginIt != db->userMetadata.end()) {
*userlistModified = true;
2014-06-21 23:33:44 +01:00
std::set<loot::Tag> tags(pluginIt->Tags());
for (const auto &tag : tags) {
if (tag.IsAddition())
tagsAdded.insert(tag.Name());
else
2014-06-21 23:33:44 +01:00
tagsRemoved.insert(tag.Name());
}
}
2014-01-12 16:33:28 +00:00
if ((!tagsAdded.empty() || !tagsRemoved.empty()) && db->bashTagMap.empty()) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_no_tag_map, "No Bash Tag map has been previously generated.");
2014-01-12 16:33:28 +00:00
}
std::vector<unsigned int> tagsAddedIDs, tagsRemovedIDs;
2014-06-21 23:33:44 +01:00
for (const auto &tagNames: tagsAdded) {
const auto mapIter(db->bashTagMap.find(tagNames));
if (mapIter != db->bashTagMap.end())
tagsAddedIDs.push_back(mapIter->second);
}
2014-06-21 23:33:44 +01:00
for (const auto &tagNames : tagsRemoved) {
const auto mapIter(db->bashTagMap.find(tagNames));
if (mapIter != db->bashTagMap.end())
2014-06-21 23:33:44 +01:00
tagsAddedIDs.push_back(mapIter->second);
}
//Allocate memory.
size_t numAdded = tagsAddedIDs.size();
size_t numRemoved = tagsRemovedIDs.size();
try {
if (numAdded != 0) {
db->extAddedTagIds = new uint32_t[numAdded];
for (size_t i=0; i < numAdded; i++)
db->extAddedTagIds[i] = tagsAddedIDs[i];
}
if (numRemoved != 0) {
db->extRemovedTagIds = new uint32_t[numRemoved];
for (size_t i=0; i < numRemoved; i++)
db->extRemovedTagIds[i] = tagsRemovedIDs[i];
}
} catch (std::bad_alloc& e) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_no_mem, e.what());
}
//Set outputs.
*tagIds_added = db->extAddedTagIds;
*tagIds_removed = db->extRemovedTagIds;
*numTags_added = numAdded;
*numTags_removed = numRemoved;
2014-03-06 20:18:49 +00:00
return loot_ok;
}
// Returns the messages attached to the given plugin. Messages are valid until Load,
2014-03-06 20:24:05 +00:00
// loot_destroy_db or loot_get_plugin_messages are next called. plugin is case-insensitive.
2014-06-21 23:45:40 +01:00
// If no messages are attached, *messages will be nullptr and numMessages will equal 0.
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_get_plugin_messages (loot_db db, const char * const plugin,
loot_message ** const messages,
size_t * const numMessages) {
2014-06-21 23:45:40 +01:00
if (db == nullptr || plugin == nullptr || messages == nullptr || numMessages == nullptr)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null pointer passed.");
//Clear existing array allocation.
2014-06-21 23:45:40 +01:00
if (db->extMessageArray != nullptr) {
for (size_t i=0; i < db->extMessageArraySize; ++i) {
delete [] db->extMessageArray[i].message;
}
delete [] db->extMessageArray;
2014-06-21 23:45:40 +01:00
db->extMessageArray = nullptr;
}
//Initialise output.
2014-06-21 23:45:40 +01:00
*messages = nullptr;
*numMessages = 0;
2014-03-06 20:18:49 +00:00
std::list<loot::Message> pluginMessages;
std::list<loot::Plugin>::iterator pluginIt = std::find(db->metadata.begin(), db->metadata.end(), loot::Plugin(plugin));
2013-01-12 23:30:57 +00:00
if (pluginIt != db->metadata.end()) {
pluginMessages = pluginIt->Messages();
}
2014-03-06 20:18:49 +00:00
pluginIt = std::find(db->userMetadata.begin(), db->userMetadata.end(), loot::Plugin(plugin));
2013-01-12 23:30:57 +00:00
if (pluginIt != db->userMetadata.end()) {
2014-03-06 20:18:49 +00:00
std::list<loot::Message> temp = pluginIt->Messages();
pluginMessages.insert(pluginMessages.end(), temp.begin(), temp.end());
2013-01-12 23:30:57 +00:00
}
db->extMessageArraySize = pluginMessages.size();
try {
2014-03-06 20:18:49 +00:00
db->extMessageArray = new loot_message[db->extMessageArraySize];
2013-01-12 23:30:57 +00:00
int i = 0;
2014-06-21 23:33:44 +01:00
for (const auto &message: pluginMessages) {
db->extMessageArray[i].type = message.Type();
db->extMessageArray[i].message = ToNewCString(message.ChooseContent(loot::Language::any).Str());
2013-01-12 23:30:57 +00:00
}
} catch (std::bad_alloc& e) {
2014-03-06 20:18:49 +00:00
return c_error(loot_error_no_mem, e.what());
2013-01-12 23:30:57 +00:00
}
*messages = db->extMessageArray;
*numMessages = db->extMessageArraySize;
2014-03-06 20:18:49 +00:00
return loot_ok;
}
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_get_dirty_info(loot_db db, const char * const plugin, unsigned int * const needsCleaning) {
2014-06-21 23:45:40 +01:00
if (db == nullptr || plugin == nullptr || needsCleaning == nullptr)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null pointer passed.");
2014-03-06 20:18:49 +00:00
*needsCleaning = loot_needs_cleaning_unknown;
2014-01-12 16:33:28 +00:00
//Get all dirty info.
2014-03-06 20:18:49 +00:00
std::set<loot::PluginDirtyInfo> dirtyInfo;
std::list<loot::Plugin>::iterator pluginIt = std::find(db->metadata.begin(), db->metadata.end(), loot::Plugin(plugin));
if (pluginIt != db->metadata.end()) {
2014-01-12 16:33:28 +00:00
dirtyInfo = pluginIt->DirtyInfo();
}
2014-03-06 20:18:49 +00:00
pluginIt = std::find(db->userMetadata.begin(), db->userMetadata.end(), loot::Plugin(plugin));
if (pluginIt != db->userMetadata.end()) {
2014-03-06 20:18:49 +00:00
std::set<loot::PluginDirtyInfo> temp = pluginIt->DirtyInfo();
2014-01-12 16:33:28 +00:00
dirtyInfo.insert(temp.begin(), temp.end());
}
2014-01-12 16:33:28 +00:00
if (!dirtyInfo.empty()) {
2014-03-06 20:18:49 +00:00
*needsCleaning = loot_needs_cleaning_yes;
}
2014-03-06 20:18:49 +00:00
return loot_ok;
}
// Writes a minimal masterlist that only contains mods that have Bash Tag suggestions,
// and/or dirty messages, plus the Tag suggestions and/or messages themselves and their
// conditions, in order to create the Wrye Bash taglist. outputFile is the path to use
// for output. If outputFile already exists, it will only be overwritten if overwrite is true.
2014-03-06 20:18:49 +00:00
LOOT_API unsigned int loot_write_minimal_list (loot_db db, const char * const outputFile, const bool overwrite) {
2014-06-21 23:45:40 +01:00
if (db == nullptr || outputFile == nullptr)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Null pointer passed.");
if (boost::filesystem::exists(outputFile) && !overwrite)
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Output file exists but overwrite is not set to true.");
2014-03-06 20:18:49 +00:00
std::list<loot::Plugin> temp = db->metadata;
2014-06-21 23:33:44 +01:00
for (auto &plugin: temp) {
loot::Plugin p(plugin.Name());
p.Tags(plugin.Tags());
p.DirtyInfo(plugin.DirtyInfo());
2014-06-21 23:33:44 +01:00
plugin = p;
}
YAML::Emitter yout;
yout.SetIndent(2);
yout << YAML::BeginMap
<< YAML::Key << "plugins" << YAML::Value << temp
<< YAML::EndMap;
2013-07-22 20:44:47 +01:00
boost::filesystem::path p(outputFile);
2014-03-06 20:18:49 +00:00
loot::ofstream out(p);
if (out.fail())
2014-03-06 20:18:49 +00:00
return c_error(loot_error_invalid_args, "Couldn't open output file");
out << yout.c_str();
out.close();
2014-03-06 20:18:49 +00:00
return loot_ok;
}