diff --git a/src/api/api.cpp b/src/api/api.cpp index 457b0bbe..64093c0d 100644 --- a/src/api/api.cpp +++ b/src/api/api.cpp @@ -89,7 +89,7 @@ const unsigned int loot_needs_cleaning_yes = 1; const unsigned int loot_needs_cleaning_unknown = 2; struct _loot_db_int : public loot::Game { - _loot_db_int(const unsigned int clientGame, const std::string& gamePath) + _loot_db_int(const unsigned int clientGame, const std::string& gamePath, const boost::filesystem::path& gameLocalDataPath) : Game(clientGame), extTagMap(nullptr), extAddedTagIds(nullptr), @@ -100,7 +100,7 @@ struct _loot_db_int : public loot::Game { extStringArraySize(0), extRevisionID(nullptr), extRevisionDate(nullptr) { - this->SetDetails("", "", "", "", gamePath, "").Init(false); + this->SetDetails("", "", "", "", gamePath, "").Init(false, gameLocalDataPath); } ~_loot_db_int() { @@ -229,7 +229,10 @@ LOOT_API unsigned int loot_get_version(unsigned int * const versionMajor, unsign // plugins.txt and loadorder.txt (if they both exist) are in sync. If // dataPath == nullptr then the API will attempt to detect the data path of // the specified game. -LOOT_API unsigned int loot_create_db(loot_db * const db, const unsigned int clientGame, const char * const gamePath) { +LOOT_API unsigned int loot_create_db(loot_db * const db, + const unsigned int clientGame, + const char * const gamePath, + const char * const gameLocalPath) { if (db == nullptr || (clientGame != loot_game_tes4 && clientGame != loot_game_tes5 && clientGame != loot_game_fo3 && clientGame != loot_game_fonv)) return c_error(loot_error_invalid_args, "Null pointer passed."); @@ -244,9 +247,13 @@ LOOT_API unsigned int loot_create_db(loot_db * const db, const unsigned int clie if (gamePath != nullptr) game_path = gamePath; + boost::filesystem::path game_local_path = ""; + if (gameLocalPath != nullptr) + game_local_path = gameLocalPath; + loot_db retVal = {0}; try { - retVal = new _loot_db_int(clientGame, game_path); + retVal = new _loot_db_int(clientGame, game_path, game_local_path); } catch (loot::error& e) { return c_error(e); diff --git a/src/api/api.h b/src/api/api.h index 76eb9c2d..9c390e97 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -326,11 +326,18 @@ extern "C" * The relative or absolute path to the game folder, or `NULL`. * If `NULL`, the API will attempt to detect the data path of the * specified game. + * @param gameLocalPath + * The relative or absolute path to the game's folder in + * `%LOCALAPPDATA%`, or `NULL`. If `NULL`, the API will attempt to + * look up the path that `%LOCALAPPDATA%` corresponds to. This + * parameter is provided so that systems lacking that environmental + * variable (eg. Linux) can still use the API. * @returns A return code. */ LOOT_API unsigned int loot_create_db(loot_db * const db, const unsigned int clientGame, - const char * const gamePath); + const char * const gamePath, + const char * const gameLocalPath); /** * @brief Destroy an existing database handle. diff --git a/src/backend/game.cpp b/src/backend/game.cpp index 3ec39588..48dbba2a 100644 --- a/src/backend/game.cpp +++ b/src/backend/game.cpp @@ -352,7 +352,7 @@ namespace loot { return *this; } - Game& Game::Init(bool createFolder) { + Game& Game::Init(bool createFolder, const boost::filesystem::path& gameLocalAppData) { if (id != Game::tes4 && id != Game::tes5 && id != Game::fo3 && id != Game::fonv) { throw error(error::invalid_args, lc::translate("Invalid game ID supplied.").str()); } @@ -381,6 +381,9 @@ namespace loot { throw error(error::path_not_found, lc::translate("Game path could not be detected.").str()); } + // Set the path to the game's folder in %LOCALAPPDATA%. + _gameLocalDataPath = gameLocalAppData; + RefreshActivePluginsList(); if (createFolder) { @@ -469,14 +472,19 @@ namespace loot { char ** pluginArr; size_t pluginArrSize; int ret; + + const char * gameLocalDataPath = nullptr; + if (!_gameLocalDataPath.empty()) + gameLocalDataPath = _gameLocalDataPath.string().c_str(); + if (Id() == Game::tes4) - ret = lo_create_handle(&gh, LIBLO_GAME_TES4, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_TES4, gamePath.string().c_str(), gameLocalDataPath); else if (Id() == Game::tes5) - ret = lo_create_handle(&gh, LIBLO_GAME_TES5, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_TES5, gamePath.string().c_str(), gameLocalDataPath); else if (Id() == Game::fo3) - ret = lo_create_handle(&gh, LIBLO_GAME_FO3, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_FO3, gamePath.string().c_str(), gameLocalDataPath); else if (Id() == Game::fonv) - ret = lo_create_handle(&gh, LIBLO_GAME_FNV, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_FNV, gamePath.string().c_str(), gameLocalDataPath); else ret = LIBLO_ERROR_INVALID_ARGS; @@ -552,14 +560,19 @@ namespace loot { size_t pluginArrSize; int ret; + + const char * gameLocalDataPath = nullptr; + if (!_gameLocalDataPath.empty()) + gameLocalDataPath = _gameLocalDataPath.string().c_str(); + if (Id() == Game::tes4) - ret = lo_create_handle(&gh, LIBLO_GAME_TES4, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_TES4, gamePath.string().c_str(), gameLocalDataPath); else if (Id() == Game::tes5) - ret = lo_create_handle(&gh, LIBLO_GAME_TES5, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_TES5, gamePath.string().c_str(), gameLocalDataPath); else if (Id() == Game::fo3) - ret = lo_create_handle(&gh, LIBLO_GAME_FO3, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_FO3, gamePath.string().c_str(), gameLocalDataPath); else if (Id() == Game::fonv) - ret = lo_create_handle(&gh, LIBLO_GAME_FNV, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_FNV, gamePath.string().c_str(), gameLocalDataPath); else ret = LIBLO_ERROR_INVALID_ARGS; @@ -628,14 +641,19 @@ namespace loot { lo_game_handle gh = nullptr; int ret; + + const char * gameLocalDataPath = nullptr; + if (!_gameLocalDataPath.empty()) + gameLocalDataPath = _gameLocalDataPath.string().c_str(); + if (Id() == Game::tes4) - ret = lo_create_handle(&gh, LIBLO_GAME_TES4, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_TES4, gamePath.string().c_str(), gameLocalDataPath); else if (Id() == Game::tes5) - ret = lo_create_handle(&gh, LIBLO_GAME_TES5, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_TES5, gamePath.string().c_str(), gameLocalDataPath); else if (Id() == Game::fo3) - ret = lo_create_handle(&gh, LIBLO_GAME_FO3, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_FO3, gamePath.string().c_str(), gameLocalDataPath); else if (Id() == Game::fonv) - ret = lo_create_handle(&gh, LIBLO_GAME_FNV, gamePath.string().c_str()); + ret = lo_create_handle(&gh, LIBLO_GAME_FNV, gamePath.string().c_str(), gameLocalDataPath); else ret = LIBLO_ERROR_INVALID_ARGS; @@ -710,7 +728,7 @@ namespace loot { for (size_t i = 0; i < pluginArrSize; i++) delete[] pluginArr[i]; delete[] pluginArr; - throw e; + throw; } for (size_t i = 0; i < pluginArrSize; i++) diff --git a/src/backend/game.h b/src/backend/game.h index 1a403ade..aad5d7ea 100644 --- a/src/backend/game.h +++ b/src/backend/game.h @@ -108,7 +108,7 @@ namespace loot { Game& SetDetails(const std::string& name, const std::string& masterFile, const std::string& repositoryURL, const std::string& repositoryBranch, const std::string& path, const std::string& registry); - Game& Init(bool createFolder); + Game& Init(bool createFolder, const boost::filesystem::path& gameLocalAppData = ""); bool IsInstalled() const; @@ -170,6 +170,7 @@ namespace loot { std::string _repositoryBranch; boost::filesystem::path gamePath; //Path to the game's folder. + boost::filesystem::path _gameLocalDataPath; // Path to the game's folder in %LOCALAPPDATA%. std::unordered_set activePlugins; //Holds lowercased strings.