mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Some tidying up. Added game handling, some helper functions from BOSSv2, some globals, the API files, the beginnings of the sorting algorithm stuff and some BOSS data cache file reading/writing.
This commit is contained in:
+6
-3
@@ -11,10 +11,10 @@
|
||||
cmake_minimum_required (VERSION 2.8.9)
|
||||
project (boss)
|
||||
|
||||
set (BOSS_SRC "${CMAKE_SOURCE_DIR}/src/metadata.cpp")
|
||||
set (BOSS_SRC "${CMAKE_SOURCE_DIR}/src/metadata.cpp" "${CMAKE_SOURCE_DIR}/src/game.cpp" "${CMAKE_SOURCE_DIR}/src/helpers.cpp")
|
||||
|
||||
# Include source and library directories.
|
||||
include_directories ("${BOSS_LIBS_DIR}/boost" "${BOSS_LIBS_DIR}/yaml-cpp/include" "${CMAKE_SOURCE_DIR}/src")
|
||||
include_directories ("${BOSS_LIBS_DIR}/alphanum" "${BOSS_LIBS_DIR}/utf8" "${BOSS_LIBS_DIR}/boost" "${BOSS_LIBS_DIR}/yaml-cpp/include" "${CMAKE_SOURCE_DIR}/src")
|
||||
|
||||
##############################
|
||||
# Platform-Specific Settings
|
||||
@@ -37,7 +37,7 @@ ENDIF ()
|
||||
|
||||
# Settings when compiling and cross-compiling on Linux.
|
||||
IF (CMAKE_HOST_SYSTEM_NAME MATCHES "Linux")
|
||||
set (BOSS_LIBS yaml-cpp)
|
||||
set (BOSS_LIBS yaml-cpp boost_filesystem boost_system boost_regex)
|
||||
set (CMAKE_C_FLAGS "-m${BOSS_ARCH}")
|
||||
set (CMAKE_CXX_FLAGS "-m${BOSS_ARCH}")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "-static-libstdc++ -static-libgcc")
|
||||
@@ -45,15 +45,18 @@ IF (CMAKE_HOST_SYSTEM_NAME MATCHES "Linux")
|
||||
set (CMAKE_MODULE_LINKER_FLAGS "-static-libstdc++ -static-libgcc")
|
||||
|
||||
link_directories ("${BOSS_LIBS_DIR}/yaml-cpp/build/")
|
||||
link_directories ("${BOSS_LIBS_DIR}/boost/stage-${BOSS_ARCH}/lib")
|
||||
|
||||
IF (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
link_directories ("${BOSS_LIBS_DIR}/yaml-cpp/build/")
|
||||
link_directories ("${BOSS_LIBS_DIR}/boost/stage-mingw-${BOSS_ARCH}/lib")
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
||||
# Settings when not cross-compiling.
|
||||
IF (CMAKE_SYSTEM_NAME MATCHES CMAKE_HOST_SYSTEM_NAME)
|
||||
link_directories ("${BOSS_LIBS_DIR}/yaml-cpp/build/")
|
||||
link_directories ("${BOSS_LIBS_DIR}/boost/stage-${BOSS_ARCH}/lib")
|
||||
ENDIF ()
|
||||
|
||||
##############################
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/* BOSS
|
||||
|
||||
A plugin load order optimiser for games that use the esp/esm plugin system.
|
||||
|
||||
Copyright (C) 2012 WrinklyNinja
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "api.h"
|
||||
|
||||
//////////////////////////////
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_get_error_message (uint8_t ** message) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// Version Functions
|
||||
//////////////////////////////
|
||||
|
||||
// Returns whether this version of BOSS supports the API from the given
|
||||
// BOSS version. Abstracts BOSS API stability policy away from clients.
|
||||
BOSS_API bool boss_is_compatible (const uint32_t versionMajor, const uint32_t versionMinor, const uint32_t versionPatch) {
|
||||
|
||||
}
|
||||
|
||||
// Returns the version string for this version of BOSS.
|
||||
// The string exists until this function is called again or until
|
||||
// CleanUpAPI is called.
|
||||
BOSS_API uint32_t boss_get_version (uint32_t * versionMajor, uint32_t * versionMinor, uint32_t * versionPatch) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// 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
|
||||
// dataPath == NULL then the API will attempt to detect the data path of
|
||||
// the specified game.
|
||||
BOSS_API uint32_t boss_create_db (boss_db * db, const uint32_t clientGame, const uint8_t * gamePath) {
|
||||
|
||||
}
|
||||
|
||||
// Destroys the given DB, freeing any memory allocated as part of its use.
|
||||
BOSS_API void boss_destroy_db (boss_db db) {
|
||||
|
||||
}
|
||||
|
||||
// Frees memory allocated to version and error strings.
|
||||
BOSS_API void boss_cleanup () {
|
||||
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_load_lists (boss_db db, const uint8_t * masterlistPath,
|
||||
const uint8_t * userlistPath) {
|
||||
|
||||
}
|
||||
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_eval_lists (boss_db db) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_get_tag_map (boss_db db, boss_tag ** tagMap, size_t * numTags) {
|
||||
|
||||
}
|
||||
|
||||
// Returns arrays of Bash Tag UIDs for Bash Tags suggested for addition and removal
|
||||
// by BOSS'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)
|
||||
// will be NULL. The userlistModified bool is true if the userlist contains Bash Tag
|
||||
// suggestion message additions.
|
||||
BOSS_API uint32_t boss_get_plugin_tags (boss_db db, const uint8_t * plugin,
|
||||
uint32_t ** tagIds_added,
|
||||
size_t * numTags_added,
|
||||
uint32_t **tagIds_removed,
|
||||
size_t *numTags_removed,
|
||||
bool * userlistModified) {
|
||||
|
||||
}
|
||||
|
||||
// Returns the messages attached to the given plugin. Messages are valid until Load,
|
||||
// DestroyBossDb or GetPluginMessages are next called. plugin is case-insensitive.
|
||||
// If no messages are attached, *messages will be NULL and numMessages will equal 0.
|
||||
BOSS_API uint32_t boss_get_plugin_messages (boss_db db, const uint8_t * plugin,
|
||||
boss_message ** messages,
|
||||
size_t * numMessages) {
|
||||
|
||||
}
|
||||
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_write_minimal_list (boss_db db, const uint8_t * outputFile, const bool overwrite) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/* BOSS
|
||||
|
||||
A plugin load order optimiser for games that use the esp/esm plugin system.
|
||||
|
||||
Copyright (C) 2012 WrinklyNinja
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __BOSS_API_H__
|
||||
#define __BOSS_API_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
//MSVC doesn't support C99, so do the stdbool.h definitions ourselves.
|
||||
//START OF stdbool.h DEFINITIONS.
|
||||
# ifndef __cplusplus
|
||||
# define bool _Bool
|
||||
# define true 1
|
||||
# define false 0
|
||||
# endif
|
||||
# define __bool_true_false_are_defined 1
|
||||
//END OF stdbool.h DEFINITIONS.
|
||||
#else
|
||||
# include <stdbool.h>
|
||||
#endif
|
||||
|
||||
// set up dll import/export decorators
|
||||
// when compiling the dll on windows, ensure BOSS_API_EXPORT is defined. clients
|
||||
// that use this header do not need to define anything to import the symbols
|
||||
// properly.
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
# ifdef BOSS_API_EXPORT
|
||||
# define BOSS_API __declspec(dllexport)
|
||||
# else
|
||||
# define BOSS_API __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define BOSS_API
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Types
|
||||
////////////////////////
|
||||
|
||||
// All API strings are uint8_t* strings encoded in UTF-8. Strings returned
|
||||
// by the API should not have their memory freed by the client: the API will
|
||||
// clean up after itself.
|
||||
// All API numbers and error codes are uint32_t integers.
|
||||
|
||||
// Abstracts the definition of BOSS's internal state while still providing
|
||||
// type safety across the API.
|
||||
typedef struct _boss_db_int * boss_db;
|
||||
|
||||
// boss_tag structure gives the Unique ID number (UID) for each Bash Tag and
|
||||
// the corresponding Tag name string.
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
const uint8_t * name; // don't use char for utf-8 since char can be signed
|
||||
} boss_tag;
|
||||
|
||||
// boss_message structure gives the type of message and it contents.
|
||||
typedef struct {
|
||||
uint32_t type;
|
||||
const uint8_t * message;
|
||||
} boss_message;
|
||||
|
||||
|
||||
// The following are the possible codes that the API can return.
|
||||
BOSS_API extern const uint32_t BOSS_API_OK;
|
||||
BOSS_API extern const uint32_t BOSS_API_OK_NO_UPDATE_NECESSARY;
|
||||
BOSS_API extern const uint32_t BOSS_API_WARN_BAD_FILENAME;
|
||||
BOSS_API extern const uint32_t BOSS_API_WARN_LO_MISMATCH;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_FILE_WRITE_FAIL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_FILE_DELETE_FAIL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_FILE_NOT_UTF8;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_FILE_NOT_FOUND;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_FILE_RENAME_FAIL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_TIMESTAMP_READ_FAIL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_TIMESTAMP_WRITE_FAIL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_PARSE_FAIL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_CONDITION_EVAL_FAIL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_REGEX_EVAL_FAIL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_NO_MEM;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_INVALID_ARGS;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_NETWORK_FAIL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_NO_INTERNET_CONNECTION;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_NO_TAG_MAP;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_PLUGINS_FULL;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_GAME_NOT_FOUND;
|
||||
BOSS_API extern const uint32_t BOSS_API_ERROR_PLUGIN_BEFORE_MASTER;
|
||||
BOSS_API extern const uint32_t BOSS_API_RETURN_MAX;
|
||||
|
||||
// The following are the games identifiers used by the API.
|
||||
BOSS_API extern const uint32_t BOSS_API_GAME_OBLIVION;
|
||||
BOSS_API extern const uint32_t BOSS_API_GAME_FALLOUT3;
|
||||
BOSS_API extern const uint32_t BOSS_API_GAME_FALLOUTNV;
|
||||
BOSS_API extern const uint32_t BOSS_API_GAME_NEHRIM;
|
||||
BOSS_API extern const uint32_t BOSS_API_GAME_SKYRIM;
|
||||
BOSS_API extern const uint32_t BOSS_API_GAME_MORROWIND;
|
||||
|
||||
// BOSS message types.
|
||||
BOSS_API extern const uint32_t BOSS_API_MESSAGE_SAY;
|
||||
BOSS_API extern const uint32_t BOSS_API_MESSAGE_TAG;
|
||||
BOSS_API extern const uint32_t BOSS_API_MESSAGE_REQUIREMENT;
|
||||
BOSS_API extern const uint32_t BOSS_API_MESSAGE_INCOMPATIBILITY;
|
||||
BOSS_API extern const uint32_t BOSS_API_MESSAGE_DIRTY;
|
||||
BOSS_API extern const uint32_t BOSS_API_MESSAGE_WARN;
|
||||
BOSS_API extern const uint32_t BOSS_API_MESSAGE_ERROR;
|
||||
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_get_error_message (uint8_t ** message);
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// Version Functions
|
||||
//////////////////////////////
|
||||
|
||||
// Returns whether this version of BOSS supports the API from the given
|
||||
// BOSS version. Abstracts BOSS API stability policy away from clients.
|
||||
BOSS_API bool boss_is_compatible (const uint32_t versionMajor, const uint32_t versionMinor, const uint32_t versionPatch);
|
||||
|
||||
// Returns the version string for this version of BOSS.
|
||||
// The string exists until this function is called again or until
|
||||
// CleanUpAPI is called.
|
||||
BOSS_API uint32_t boss_get_version (uint32_t * versionMajor, uint32_t * versionMinor, uint32_t * versionPatch);
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// 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
|
||||
// dataPath == NULL then the API will attempt to detect the data path of
|
||||
// the specified game.
|
||||
BOSS_API uint32_t boss_create_db (boss_db * db, const uint32_t clientGame, const uint8_t * gamePath);
|
||||
|
||||
// Destroys the given DB, freeing any memory allocated as part of its use.
|
||||
BOSS_API void boss_destroy_db (boss_db db);
|
||||
|
||||
// Frees memory allocated to version and error strings.
|
||||
BOSS_API void boss_cleanup ();
|
||||
|
||||
|
||||
///////////////////////////////////
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_load_lists (boss_db db, const uint8_t * masterlistPath,
|
||||
const uint8_t * userlistPath);
|
||||
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_eval_lists (boss_db db);
|
||||
|
||||
|
||||
//////////////////////////
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_get_tag_map (boss_db db, boss_tag ** tagMap, size_t * numTags);
|
||||
|
||||
// Returns arrays of Bash Tag UIDs for Bash Tags suggested for addition and removal
|
||||
// by BOSS'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)
|
||||
// will be NULL. The userlistModified bool is true if the userlist contains Bash Tag
|
||||
// suggestion message additions.
|
||||
BOSS_API uint32_t boss_get_plugin_tags (boss_db db, const uint8_t * plugin,
|
||||
uint32_t ** tagIds_added,
|
||||
size_t * numTags_added,
|
||||
uint32_t **tagIds_removed,
|
||||
size_t *numTags_removed,
|
||||
bool * userlistModified);
|
||||
|
||||
// Returns the messages attached to the given plugin. Messages are valid until Load,
|
||||
// DestroyBossDb or GetPluginMessages are next called. plugin is case-insensitive.
|
||||
// If no messages are attached, *messages will be NULL and numMessages will equal 0.
|
||||
BOSS_API uint32_t boss_get_plugin_messages (boss_db db, const uint8_t * plugin,
|
||||
boss_message ** messages,
|
||||
size_t * numMessages);
|
||||
|
||||
// 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.
|
||||
BOSS_API uint32_t boss_write_minimal_list (boss_db db, const uint8_t * outputFile, const bool overwrite);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/* BOSS
|
||||
|
||||
A plugin load order optimiser for games that use the esp/esm plugin system.
|
||||
|
||||
Copyright (C) 2012 WrinklyNinja
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "game.h"
|
||||
#include "globals.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#if _WIN32 || _WIN64
|
||||
# include <Windows.h>
|
||||
# include <Shlobj.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace boss {
|
||||
|
||||
Game::Game()
|
||||
: id(BOSS_GAME_AUTODETECT) {}
|
||||
|
||||
Game::Game(const uint32_t gameCode, const string path, const bool noPathInit)
|
||||
: id(gameCode) {
|
||||
if (Id() == BOSS_GAME_TES4) {
|
||||
name = "TES IV: Oblivion";
|
||||
|
||||
registryKey = "Software\\Bethesda Softworks\\Oblivion";
|
||||
registrySubKey = "Installed Path";
|
||||
|
||||
bossFolderName = "Oblivion";
|
||||
pluginsFolderName = "Data";
|
||||
} else if (Id() == BOSS_GAME_TES5) {
|
||||
name = "TES V: Skyrim";
|
||||
|
||||
registryKey = "Software\\Bethesda Softworks\\Skyrim";
|
||||
registrySubKey = "Installed Path";
|
||||
|
||||
bossFolderName = "Skyrim";
|
||||
pluginsFolderName = "Data";
|
||||
} else if (Id() == BOSS_GAME_FO3) {
|
||||
name = "Fallout 3";
|
||||
|
||||
registryKey = "Software\\Bethesda Softworks\\Fallout3";
|
||||
registrySubKey = "Installed Path";
|
||||
|
||||
bossFolderName = "Fallout 3";
|
||||
pluginsFolderName = "Data";
|
||||
} else if (Id() == BOSS_GAME_FONV) {
|
||||
name = "Fallout: New Vegas";
|
||||
|
||||
registryKey = "Software\\Bethesda Softworks\\FalloutNV";
|
||||
registrySubKey = "Installed Path";
|
||||
|
||||
bossFolderName = "Fallout New Vegas";
|
||||
pluginsFolderName = "Data";
|
||||
} else
|
||||
throw runtime_error("Invalid game ID supplied.");
|
||||
|
||||
if (!noPathInit) {
|
||||
if (path.empty()) {
|
||||
//First look for local install, then look for Registry.
|
||||
if (IsInstalledLocally())
|
||||
gamePath = "..";
|
||||
else if (RegKeyExists("HKEY_LOCAL_MACHINE", registryKey, registrySubKey))
|
||||
gamePath = fs::path(RegKeyStringValue("HKEY_LOCAL_MACHINE", registryKey, registrySubKey));
|
||||
else
|
||||
throw runtime_error("Game path could not be detected.");
|
||||
} else
|
||||
gamePath = fs::path(path);
|
||||
}
|
||||
}
|
||||
|
||||
bool Game::IsInstalled() const {
|
||||
return (IsInstalledLocally() || RegKeyExists("HKEY_LOCAL_MACHINE", registryKey, registrySubKey));
|
||||
}
|
||||
|
||||
bool Game::IsInstalledLocally() const {
|
||||
return fs::exists(fs::path("..") / pluginsFolderName);
|
||||
}
|
||||
|
||||
uint32_t Game::Id() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
string Game::Name() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
fs::path Game::GamePath() const {
|
||||
return gamePath;
|
||||
}
|
||||
|
||||
fs::path Game::DataPath() const {
|
||||
return GamePath() / pluginsFolderName;
|
||||
}
|
||||
|
||||
void Game::CreateBOSSGameFolder() {
|
||||
//Make sure that the BOSS game path exists.
|
||||
try {
|
||||
if (!fs::exists(bossFolderName))
|
||||
fs::create_directory(bossFolderName);
|
||||
} catch (fs::filesystem_error e) {
|
||||
throw runtime_error("Could not create BOSS folder for game.");
|
||||
}
|
||||
}
|
||||
|
||||
//Can be used to get the location of the LOCALAPPDATA folder (and its Windows XP equivalent).
|
||||
fs::path Game::GetLocalAppDataPath() {
|
||||
#if _WIN32 || _WIN64
|
||||
HWND owner;
|
||||
TCHAR path[MAX_PATH];
|
||||
|
||||
HRESULT res = SHGetFolderPath(owner, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path);
|
||||
|
||||
if (res == S_OK)
|
||||
return fs::path(path);
|
||||
else
|
||||
return fs::path("");
|
||||
#else
|
||||
return fs::path("");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/* BOSS
|
||||
|
||||
A plugin load order optimiser for games that use the esp/esm plugin system.
|
||||
|
||||
Copyright (C) 2012 WrinklyNinja
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __BOSS_GAME__
|
||||
#define __BOSS_GAME__
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace boss {
|
||||
|
||||
class Game {
|
||||
public:
|
||||
Game(); //Sets game to BOSS_GAME_AUTODETECT, with all other vars being empty.
|
||||
Game(const uint32_t gameCode, const std::string path = "", const bool noPathInit = false); //Empty path means constructor will detect its location. If noPathInit is true, then the game's BOSS subfolder will not be created.
|
||||
|
||||
bool IsInstalled() const;
|
||||
bool IsInstalledLocally() const;
|
||||
|
||||
uint32_t Id() const;
|
||||
std::string Name() const; //Returns the game's name, eg. "TES IV: Oblivion".
|
||||
|
||||
boost::filesystem::path GamePath() const;
|
||||
boost::filesystem::path DataPath() const;
|
||||
|
||||
//Creates directory in BOSS folder for BOSS's game-specific files.
|
||||
void CreateBOSSGameFolder();
|
||||
private:
|
||||
uint32_t id;
|
||||
std::string name;
|
||||
|
||||
std::string registryKey;
|
||||
std::string registrySubKey;
|
||||
|
||||
std::string bossFolderName;
|
||||
std::string pluginsFolderName;
|
||||
|
||||
boost::filesystem::path gamePath; //Path to the game's folder.
|
||||
|
||||
//Can be used to get the location of the LOCALAPPDATA folder (and its Windows XP equivalent).
|
||||
boost::filesystem::path GetLocalAppDataPath();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
/* BOSS
|
||||
|
||||
A plugin load order optimiser for games that use the esp/esm plugin system.
|
||||
|
||||
Copyright (C) 2012 WrinklyNinja
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __BOSS_GLOBALS__
|
||||
#define __BOSS_GLOBALS__
|
||||
|
||||
const int BOSS_GAME_AUTODETECT = 0;
|
||||
const int BOSS_GAME_TES4 = 1;
|
||||
const int BOSS_GAME_TES5 = 2;
|
||||
const int BOSS_GAME_FO3 = 3;
|
||||
const int BOSS_GAME_FONV = 4;
|
||||
|
||||
const int BOSS_VERSION_MAJOR = 3;
|
||||
const int BOSS_VERSION_MINOR = 0;
|
||||
const int BOSS_VERSION_PATCH = 0;
|
||||
|
||||
#endif
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/* BOSS
|
||||
|
||||
A "one-click" program for users that quickly optimises and avoids
|
||||
detrimental conflicts in their TES IV: Oblivion, Nehrim - At Fate's Edge,
|
||||
TES V: Skyrim, Fallout 3 and Fallout: New Vegas mod load orders.
|
||||
|
||||
Copyright (C) 2009-2012 BOSS Development Team.
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
$Revision: 3184 $, $Date: 2011-08-26 20:52:13 +0100 (Fri, 26 Aug 2011) $
|
||||
*/
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
#include <boost/spirit/include/karma.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/crc.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <alphanum.hpp>
|
||||
#include "source/utf8.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sstream>
|
||||
|
||||
#if _WIN32 || _WIN64
|
||||
# include "Windows.h"
|
||||
# include "Shlobj.h"
|
||||
#endif
|
||||
|
||||
namespace boss {
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using boost::algorithm::replace_all;
|
||||
using boost::algorithm::replace_first;
|
||||
namespace karma = boost::spirit::karma;
|
||||
|
||||
//Calculate the CRC of the given file for comparison purposes.
|
||||
uint32_t GetCrc32(const fs::path& filename) {
|
||||
uint32_t chksum = 0;
|
||||
static const size_t buffer_size = 8192;
|
||||
char buffer[buffer_size];
|
||||
ifstream ifile(filename.c_str(), ios::binary);
|
||||
// LOG_TRACE("calculating CRC for: '%s'", filename.string().c_str());
|
||||
boost::crc_32_type result;
|
||||
if (ifile) {
|
||||
do {
|
||||
ifile.read(buffer, buffer_size);
|
||||
result.process_bytes(buffer, ifile.gcount());
|
||||
} while (ifile);
|
||||
chksum = result.checksum();
|
||||
} else {
|
||||
throw runtime_error("Unable to open \"" + filename.string() + "\" for CRC calculation.");
|
||||
}
|
||||
// LOG_DEBUG("CRC32('%s'): 0x%x", filename.string().c_str(), chksum);
|
||||
return chksum;
|
||||
}
|
||||
|
||||
//Converts an integer to a string using BOOST's Spirit.Karma, which is apparently a lot faster than a stringstream conversion...
|
||||
std::string IntToString(const uint32_t n) {
|
||||
string out;
|
||||
back_insert_iterator<string> sink(out);
|
||||
karma::generate(sink,karma::upper[karma::uint_],n);
|
||||
return out;
|
||||
}
|
||||
|
||||
//Converts an integer to a hex string using BOOST's Spirit.Karma, which is apparently a lot faster than a stringstream conversion...
|
||||
std::string IntToHexString(const uint32_t n) {
|
||||
string out;
|
||||
back_insert_iterator<string> sink(out);
|
||||
karma::generate(sink,karma::upper[karma::hex],n);
|
||||
return out;
|
||||
}
|
||||
|
||||
//Converts a boolean to a string representation (true/false)
|
||||
std::string BoolToString(const bool b) {
|
||||
if (b)
|
||||
return "true";
|
||||
else
|
||||
return "false";
|
||||
}
|
||||
|
||||
//Check if registry subkey exists.
|
||||
bool RegKeyExists(const std::string& keyStr, const std::string& subkey, const std::string& value) {
|
||||
return !RegKeyStringValue(keyStr, subkey, value).empty();
|
||||
}
|
||||
|
||||
//Get registry subkey value string.
|
||||
string RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value) {
|
||||
#if _WIN32 || _WIN64
|
||||
HKEY hKey, key;
|
||||
DWORD BufferSize = 4096;
|
||||
wchar_t val[4096];
|
||||
|
||||
if (keyStr == "HKEY_CLASSES_ROOT")
|
||||
key = HKEY_CLASSES_ROOT;
|
||||
else if (keyStr == "HKEY_CURRENT_CONFIG")
|
||||
key = HKEY_CURRENT_CONFIG;
|
||||
else if (keyStr == "HKEY_CURRENT_USER")
|
||||
key = HKEY_CURRENT_USER;
|
||||
else if (keyStr == "HKEY_LOCAL_MACHINE")
|
||||
key = HKEY_LOCAL_MACHINE;
|
||||
else if (keyStr == "HKEY_USERS")
|
||||
key = HKEY_USERS;
|
||||
|
||||
LONG ret = RegOpenKeyEx(key, fs::path(subkey).wstring().c_str(), 0, KEY_READ|KEY_WOW64_32KEY, &hKey);
|
||||
|
||||
if (ret == ERROR_SUCCESS) {
|
||||
ret = RegQueryValueEx(hKey, fs::path(value).wstring().c_str(), NULL, NULL, (LPBYTE)&val, &BufferSize);
|
||||
RegCloseKey(hKey);
|
||||
|
||||
if (ret == ERROR_SUCCESS)
|
||||
return fs::path(val).string(); //Easiest way to convert from wide to narrow character strings.
|
||||
else
|
||||
return "";
|
||||
} else
|
||||
return "";
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// Version Class Functions
|
||||
//////////////////////////////
|
||||
|
||||
Version::Version() {}
|
||||
|
||||
Version::Version(const char * ver)
|
||||
: verString(ver) {}
|
||||
|
||||
Version::Version(const std::string ver)
|
||||
: verString(ver) {}
|
||||
|
||||
Version::Version(const fs::path file) {
|
||||
// LOG_TRACE("extracting version from '%s'", file.string().c_str());
|
||||
#if _WIN32 || _WIN64
|
||||
DWORD dummy = 0;
|
||||
DWORD size = GetFileVersionInfoSize(file.wstring().c_str(), &dummy);
|
||||
|
||||
if (size > 0) {
|
||||
LPBYTE point = new BYTE[size];
|
||||
UINT uLen;
|
||||
VS_FIXEDFILEINFO *info;
|
||||
string ver;
|
||||
|
||||
GetFileVersionInfo(file.wstring().c_str(),0,size,point);
|
||||
|
||||
VerQueryValue(point,L"\\",(LPVOID *)&info,&uLen);
|
||||
|
||||
DWORD dwLeftMost = HIWORD(info->dwFileVersionMS);
|
||||
DWORD dwSecondLeft = LOWORD(info->dwFileVersionMS);
|
||||
DWORD dwSecondRight = HIWORD(info->dwFileVersionLS);
|
||||
DWORD dwRightMost = LOWORD(info->dwFileVersionLS);
|
||||
|
||||
delete [] point;
|
||||
|
||||
verString = IntToString(dwLeftMost) + '.' + IntToString(dwSecondLeft) + '.' + IntToString(dwSecondRight) + '.' + IntToString(dwRightMost);
|
||||
}
|
||||
#else
|
||||
// ensure filename has no quote characters in it to avoid command injection attacks
|
||||
if (string::npos == file.string().find('"')) {
|
||||
// LOG_WARN("filename has embedded quotes; skipping to avoid command injection: '%s'", file.string().c_str());
|
||||
// } else {
|
||||
// command mostly borrowed from the gnome-exe-thumbnailer.sh script
|
||||
// wrestool is part of the icoutils package
|
||||
string cmd = "wrestool --extract --raw --type=version \"" + file.string() + "\" | tr '\\0, ' '\\t.\\0' | sed 's/\\t\\t/_/g' | tr -c -d '[:print:]' | sed -r 's/.*Version[^0-9]*([0-9]+(\\.[0-9]+)+).*/\\1/'";
|
||||
|
||||
FILE *fp = popen(cmd.c_str(), "r");
|
||||
|
||||
// read out the version string
|
||||
static const uint32_t BUFSIZE = 32;
|
||||
char buf[BUFSIZE];
|
||||
if (NULL != fgets(buf, BUFSIZE, fp)) {
|
||||
/* LOG_DEBUG("failed to extract version from '%s'", file.string().c_str());
|
||||
}
|
||||
else {
|
||||
*/ verString = string(buf);
|
||||
// LOG_DEBUG("extracted version from '%s': %s", file.string().c_str(), retVal.c_str());
|
||||
}
|
||||
pclose(fp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
string Version::AsString() const {
|
||||
return verString;
|
||||
}
|
||||
|
||||
bool Version::operator < (Version ver) {
|
||||
//Version string could have a wide variety of formats. Use regex to choose specific comparison types.
|
||||
|
||||
boost::regex reg1("(\\d+\\.?)+"); //a.b.c.d.e.f.... where the letters are all integers, and 'a' is the shortest possible match.
|
||||
|
||||
//boost::regex reg2("(\\d+\\.?)+([a-zA-Z\\-]+(\\d+\\.?)*)+"); //Matches a mix of letters and numbers - from "0.99.xx", "1.35Alpha2", "0.9.9MB8b1", "10.52EV-D", "1.62EV" to "10.0EV-D1.62EV".
|
||||
|
||||
if (boost::regex_match(verString, reg1) && boost::regex_match(ver.AsString(), reg1)) {
|
||||
//First type: numbers separated by periods. If two versions have a different number of numbers, then the shorter should be padded
|
||||
//with zeros. An arbitrary number of numbers should be supported.
|
||||
istringstream parser1(verString);
|
||||
istringstream parser2(ver.AsString());
|
||||
while (parser1.good() || parser2.good()) {
|
||||
//Check if each stringstream is OK for i/o before doing anything with it. If not, replace its extracted value with a 0.
|
||||
uint32_t n1, n2;
|
||||
if (parser1.good()) {
|
||||
parser1 >> n1;
|
||||
parser1.get();
|
||||
} else
|
||||
n1 = 0;
|
||||
if (parser2.good()) {
|
||||
parser2 >> n2;
|
||||
parser2.get();
|
||||
} else
|
||||
n2 = 0;
|
||||
if (n1 < n2)
|
||||
return true;
|
||||
else if (n1 > n2)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
//Wacky format. Use the Alphanum Algorithm. (what a name!)
|
||||
return (doj::alphanum_comp(verString, ver.AsString()) < 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool Version::operator > (Version ver) {
|
||||
return (*this != ver && !(*this < ver));
|
||||
}
|
||||
|
||||
bool Version::operator >= (Version ver) {
|
||||
return (*this == ver || *this > ver);
|
||||
}
|
||||
|
||||
bool Version::operator == (Version ver) {
|
||||
return (verString == ver.AsString());
|
||||
}
|
||||
|
||||
bool Version::operator != (Version ver) {
|
||||
return !(*this == ver);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/* BOSS
|
||||
|
||||
A "one-click" program for users that quickly optimises and avoids
|
||||
detrimental conflicts in their TES IV: Oblivion, Nehrim - At Fate's Edge,
|
||||
TES V: Skyrim, Fallout 3 and Fallout: New Vegas mod load orders.
|
||||
|
||||
Copyright (C) 2009-2012 BOSS Development Team.
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
$Revision: 3163 $, $Date: 2011-08-21 22:03:18 +0100 (Sun, 21 Aug 2011) $
|
||||
*/
|
||||
|
||||
#ifndef __BOSS_HELPERS__
|
||||
#define __BOSS_HELPERS__
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace boss {
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Helper functions
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//Calculate the CRC of the given file for comparison purposes.
|
||||
uint32_t GetCrc32(const fs::path& filename);
|
||||
|
||||
//Converts an integer to a string using BOOST's Spirit.Karma. Faster than a stringstream conversion.
|
||||
std::string IntToString(const uint32_t n);
|
||||
|
||||
//Converts an integer to a hex string using BOOST's Spirit.Karma. Faster than a stringstream conversion.
|
||||
std::string IntToHexString(const uint32_t n);
|
||||
|
||||
//Converts a boolean to a string representation (true/false)
|
||||
std::string BoolToString(const bool b);
|
||||
|
||||
//Check if registry subkey exists.
|
||||
bool RegKeyExists(const std::string& keyStr, const std::string& subkey, const std::string& value);
|
||||
|
||||
//Get registry subkey value string.
|
||||
std::string RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value);
|
||||
|
||||
//Version class for more robust version comparisons.
|
||||
class Version {
|
||||
private:
|
||||
std::string verString;
|
||||
public:
|
||||
Version();
|
||||
Version(const char * ver);
|
||||
Version(const std::string ver);
|
||||
Version(const fs::path file);
|
||||
|
||||
std::string AsString() const;
|
||||
|
||||
bool operator > (Version);
|
||||
bool operator < (Version);
|
||||
bool operator >= (Version);
|
||||
bool operator == (Version);
|
||||
bool operator != (Version);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
+142
-19
@@ -29,84 +29,207 @@ namespace boss {
|
||||
|
||||
ConditionalData::ConditionalData() {}
|
||||
|
||||
ConditionalData::ConditionalData(const string in) : condition(in) {}
|
||||
ConditionalData::ConditionalData(const string& c) : condition(c) {}
|
||||
|
||||
bool ConditionalData::EvalCondition() const {
|
||||
ConditionalData::ConditionalData(const std::string& c, const std::string& d)
|
||||
: condition(c), data(d) {}
|
||||
|
||||
return true;
|
||||
bool ConditionalData::IsConditional() const {
|
||||
return !condition.empty();
|
||||
}
|
||||
|
||||
std::string ConditionalData::Condition() const {
|
||||
return condition;
|
||||
}
|
||||
|
||||
std::string ConditionalData::Data() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
void ConditionalData::Data(const std::string& d) {
|
||||
data = d;
|
||||
}
|
||||
|
||||
Message::Message() {}
|
||||
|
||||
Message::Message(const std::string& t, const std::string& cont)
|
||||
: type(t), ConditionalData("", cont) {}
|
||||
|
||||
Message::Message(const std::string& t, const std::string& cont,
|
||||
const std::string& cond, const std::string& l)
|
||||
: type(t), language(l), ConditionalData(cond, cont) {}
|
||||
|
||||
std::string Message::Type() const {
|
||||
return type;
|
||||
}
|
||||
|
||||
std::string Message::Language() const {
|
||||
return language;
|
||||
}
|
||||
|
||||
std::string Message::Content() const {
|
||||
return Data();
|
||||
}
|
||||
|
||||
File::File() {}
|
||||
File::File(const std::string& n) : ConditionalData("", n) {}
|
||||
File::File(const std::string& n, const std::string& d, const std::string& c)
|
||||
: display(d), ConditionalData(c, n) {}
|
||||
|
||||
std::string File::Name() const {
|
||||
return Data();
|
||||
}
|
||||
|
||||
std::string File::DisplayName() const {
|
||||
return display;
|
||||
}
|
||||
|
||||
Tag::Tag() : addTag(true) {}
|
||||
|
||||
Tag::Tag(const string tag) {
|
||||
Tag::Tag(const string& tag) {
|
||||
string data;
|
||||
if (tag[0] == '-') {
|
||||
addTag = false;
|
||||
name = tag.substr(1);
|
||||
data = tag.substr(1);
|
||||
} else {
|
||||
addTag = true;
|
||||
name = tag;
|
||||
data = tag;
|
||||
}
|
||||
Data(data);
|
||||
}
|
||||
|
||||
Tag::Tag(const string condition, const string tag) : ConditionalData(condition) {
|
||||
Tag::Tag(const string& tag, const string& condition) : ConditionalData(condition) {
|
||||
string data;
|
||||
if (tag[0] == '-') {
|
||||
addTag = false;
|
||||
name = tag.substr(1);
|
||||
data = tag.substr(1);
|
||||
} else {
|
||||
addTag = true;
|
||||
name = tag;
|
||||
data = tag;
|
||||
}
|
||||
Data(data);
|
||||
}
|
||||
|
||||
bool Tag::IsAddition() const {
|
||||
return addTag;
|
||||
}
|
||||
|
||||
string Tag::Data() const {
|
||||
string Tag::PrefixedName() const {
|
||||
if (addTag)
|
||||
return name;
|
||||
return Name();
|
||||
else
|
||||
return "-" + name;
|
||||
return "-" + Name();
|
||||
}
|
||||
|
||||
void Plugin::EvalAllConditions() {
|
||||
std::string Tag::Name() const {
|
||||
return Data();
|
||||
}
|
||||
|
||||
Plugin::Plugin() : enabled(true), priority(0) {}
|
||||
Plugin::Plugin(const std::string n) : name(n), enabled(true), priority(0) {}
|
||||
|
||||
std::string Plugin::Name() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
bool Plugin::Enabled() const {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
int Plugin::Priority() const {
|
||||
return priority;
|
||||
}
|
||||
|
||||
std::list<File> Plugin::LoadAfter() const {
|
||||
return loadAfter;
|
||||
}
|
||||
|
||||
std::list<File> Plugin::Reqs() const {
|
||||
return requirements;
|
||||
}
|
||||
|
||||
std::set<File, file_comp> Plugin::Incs() const {
|
||||
return incompatibilities;
|
||||
}
|
||||
|
||||
std::list<Message> Plugin::Messages() const {
|
||||
return messages;
|
||||
}
|
||||
|
||||
std::list<Tag> Plugin::Tags() const {
|
||||
return tags;
|
||||
}
|
||||
|
||||
void Plugin::Enabled(const bool e) {
|
||||
enabled = e;
|
||||
}
|
||||
|
||||
void Plugin::Priority(const int p) {
|
||||
priority = p;
|
||||
}
|
||||
|
||||
void Plugin::LoadAfter(const std::list<File>& l) {
|
||||
loadAfter = l;
|
||||
}
|
||||
|
||||
void Plugin::Reqs(const std::list<File>& r) {
|
||||
requirements = r;
|
||||
}
|
||||
|
||||
void Plugin::Incs(const std::set<File, file_comp>& i) {
|
||||
incompatibilities = i;
|
||||
}
|
||||
|
||||
void Plugin::Messages(const std::list<Message>& m) {
|
||||
messages = m;
|
||||
}
|
||||
|
||||
void Plugin::Tags(const std::list<Tag>& t) {
|
||||
tags = t;
|
||||
}
|
||||
|
||||
void Plugin::EvalAllConditions(const boost::filesystem::path& gamePath) {
|
||||
for (list<File>::iterator it = loadAfter.begin(); it != loadAfter.end();) {
|
||||
if (!it->EvalCondition())
|
||||
if (!it->EvalCondition(gamePath))
|
||||
it = loadAfter.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
for (list<File>::iterator it = requirements.begin(); it != requirements.end();) {
|
||||
if (!it->EvalCondition())
|
||||
if (!it->EvalCondition(gamePath))
|
||||
it = requirements.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
for (set<File, file_comp>::iterator it = incompatibilities.begin(); it != incompatibilities.end();) {
|
||||
if (!it->EvalCondition())
|
||||
if (!it->EvalCondition(gamePath))
|
||||
incompatibilities.erase(it++);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
for (list<Message>::iterator it = messages.begin(); it != messages.end();) {
|
||||
if (!it->EvalCondition())
|
||||
if (!it->EvalCondition(gamePath))
|
||||
it = messages.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
for (list<Tag>::iterator it = tags.begin(); it != tags.end();) {
|
||||
if (!it->EvalCondition())
|
||||
if (!it->EvalCondition(gamePath))
|
||||
it = tags.erase(it++);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
bool Plugin::NameOnly() const {
|
||||
bool Plugin::HasNameOnly() const {
|
||||
return priority == 0 && enabled == true && loadAfter.empty() && requirements.empty() && incompatibilities.empty() && messages.empty() && tags.empty();
|
||||
}
|
||||
|
||||
bool Plugin::IsRegexPlugin() const {
|
||||
return name.substr(name.length()-5) == "\\.esp" || name.substr(name.length()-5) == "\\.esm";
|
||||
}
|
||||
}
|
||||
|
||||
+65
-18
@@ -27,73 +27,120 @@
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace boss {
|
||||
|
||||
class ConditionalData {
|
||||
public:
|
||||
ConditionalData();
|
||||
ConditionalData(const std::string s);
|
||||
std::string condition;
|
||||
ConditionalData(const std::string& condition);
|
||||
ConditionalData(const std::string& condition, const std::string& data);
|
||||
|
||||
bool EvalCondition() const;
|
||||
bool IsConditional() const;
|
||||
bool EvalCondition(const boost::filesystem::path& gamePath) const;
|
||||
|
||||
std::string Condition() const;
|
||||
std::string Data() const;
|
||||
|
||||
void Data(const std::string& data);
|
||||
private:
|
||||
std::string condition;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
class Message : public ConditionalData {
|
||||
public:
|
||||
Message();
|
||||
Message(const std::string& type, const std::string& content);
|
||||
Message(const std::string& type, const std::string& content,
|
||||
const std::string& condition, const std::string& language);
|
||||
|
||||
std::string Type() const;
|
||||
std::string Language() const;
|
||||
std::string Content() const;
|
||||
private:
|
||||
std::string type;
|
||||
std::string language;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
class File : public ConditionalData {
|
||||
public:
|
||||
std::string name;
|
||||
File();
|
||||
File(const std::string& name);
|
||||
File(const std::string& name, const std::string& display,
|
||||
const std::string& condition);
|
||||
|
||||
std::string Name() const;
|
||||
std::string DisplayName() const;
|
||||
private:
|
||||
std::string display;
|
||||
};
|
||||
|
||||
class Tag : public ConditionalData {
|
||||
public:
|
||||
Tag();
|
||||
Tag(const std::string tag);
|
||||
Tag(const std::string condition, const std::string tag);
|
||||
|
||||
bool addTag;
|
||||
std::string name;
|
||||
Tag(const std::string& tag);
|
||||
Tag(const std::string& tag, const std::string& condition);
|
||||
|
||||
bool IsAddition() const;
|
||||
std::string Data() const; //Name with '-' in front if suggested for removal.
|
||||
std::string Name() const;
|
||||
std::string PrefixedName() const; //Name with '-' in front if suggested for removal.
|
||||
private:
|
||||
bool addTag;
|
||||
};
|
||||
|
||||
struct file_comp {
|
||||
bool operator() (const File& lhs, const File& rhs) const {
|
||||
return lhs.name < rhs.name;
|
||||
return lhs.Name() < rhs.Name();
|
||||
}
|
||||
};
|
||||
|
||||
struct tag_comp {
|
||||
bool operator() (const Tag& lhs, const Tag& rhs) const {
|
||||
return lhs.name < rhs.name;
|
||||
return lhs.Name() < rhs.Name();
|
||||
}
|
||||
};
|
||||
|
||||
class Plugin {
|
||||
public:
|
||||
Plugin();
|
||||
Plugin(const std::string name);
|
||||
|
||||
std::string Name() const;
|
||||
bool Enabled() const;
|
||||
int Priority() const;
|
||||
std::list<File> LoadAfter() const;
|
||||
std::list<File> Reqs() const;
|
||||
std::set<File, file_comp> Incs() const;
|
||||
std::list<Message> Messages() const;
|
||||
std::list<Tag> Tags() const;
|
||||
|
||||
void Enabled(const bool enabled);
|
||||
void Priority(const int priority);
|
||||
void LoadAfter(const std::list<File>& after);
|
||||
void Reqs(const std::list<File>& reqs);
|
||||
void Incs(const std::set<File, file_comp>& incs);
|
||||
void Messages(const std::list<Message>& messages);
|
||||
void Tags(const std::list<Tag>& tags);
|
||||
|
||||
void EvalAllConditions(const boost::filesystem::path& gamePath);
|
||||
bool HasNameOnly() const;
|
||||
bool IsRegexPlugin() const;
|
||||
private:
|
||||
std::string name;
|
||||
bool enabled; //Default to true.
|
||||
int priority; //Default to 0 : >0 his higher, <0 is lower priorities.
|
||||
int priority; //Default to 0 : >0 is higher, <0 is lower priorities.
|
||||
std::list<File> loadAfter;
|
||||
std::list<File> requirements;
|
||||
std::set<File, file_comp> incompatibilities;
|
||||
std::list<Message> messages;
|
||||
std::list<Tag> tags;
|
||||
|
||||
void EvalAllConditions();
|
||||
bool NameOnly() const;
|
||||
};
|
||||
|
||||
struct plugin_comp {
|
||||
bool operator() (const Plugin& lhs, const Plugin& rhs) const {
|
||||
return lhs.name < rhs.name;
|
||||
return lhs.Name() < rhs.Name();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+148
-78
@@ -24,8 +24,21 @@
|
||||
#ifndef __BOSS_PARSERS__
|
||||
#define __BOSS_PARSERS__
|
||||
|
||||
#ifndef BOOST_SPIRIT_UNICODE
|
||||
#define BOOST_SPIRIT_UNICODE
|
||||
#endif
|
||||
|
||||
#include "metadata.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/phoenix_core.hpp>
|
||||
#include <boost/spirit/include/phoenix_operator.hpp>
|
||||
#include <boost/spirit/home/phoenix/object/construct.hpp>
|
||||
#include <boost/spirit/include/phoenix_bind.hpp>
|
||||
|
||||
namespace YAML {
|
||||
|
||||
///////////////////////
|
||||
@@ -36,10 +49,10 @@ namespace YAML {
|
||||
struct convert<boss::Message> {
|
||||
static Node encode(const boss::Message& rhs) {
|
||||
Node node;
|
||||
node["condition"] = rhs.condition;
|
||||
node["type"] = rhs.type;
|
||||
node["content"] = rhs.content;
|
||||
node["lang"] = rhs.language;
|
||||
node["condition"] = rhs.Condition();
|
||||
node["type"] = rhs.Type();
|
||||
node["content"] = rhs.Content();
|
||||
node["lang"] = rhs.Language();
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -47,14 +60,17 @@ namespace YAML {
|
||||
if(!node.IsMap())
|
||||
return false;
|
||||
|
||||
std::string condition, type, content, language;
|
||||
if (node["condition"])
|
||||
rhs.condition = node["condition"].as<std::string>();
|
||||
condition = node["condition"].as<std::string>();
|
||||
if (node["type"])
|
||||
rhs.type = node["type"].as<std::string>();
|
||||
type = node["type"].as<std::string>();
|
||||
if (node["content"])
|
||||
rhs.content = node["content"].as<std::string>();
|
||||
content = node["content"].as<std::string>();
|
||||
if (node["lang"])
|
||||
rhs.language = node["lang"].as<std::string>();
|
||||
language = node["lang"].as<std::string>();
|
||||
|
||||
rhs = boss::Message(type, content, condition, language);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -63,23 +79,24 @@ namespace YAML {
|
||||
struct convert<boss::File> {
|
||||
static Node encode(const boss::File& rhs) {
|
||||
Node node;
|
||||
node["condition"] = rhs.condition;
|
||||
node["name"] = rhs.name;
|
||||
node["display"] = rhs.display;
|
||||
node["condition"] = rhs.Condition();
|
||||
node["name"] = rhs.Name();
|
||||
node["display"] = rhs.DisplayName();
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, boss::File& rhs) {
|
||||
if(node.IsMap()) {
|
||||
std::string condition, name, display;
|
||||
if (node["condition"])
|
||||
rhs.condition = node["condition"].as<std::string>();
|
||||
condition = node["condition"].as<std::string>();
|
||||
if (node["name"])
|
||||
rhs.name = node["name"].as<std::string>();
|
||||
name = node["name"].as<std::string>();
|
||||
if (node["display"])
|
||||
rhs.display = node["display"].as<std::string>();
|
||||
} else {
|
||||
rhs.name = node.as<std::string>();
|
||||
}
|
||||
display = node["display"].as<std::string>();
|
||||
rhs = boss::File(name, display, condition);
|
||||
} else
|
||||
rhs = boss::File(node.as<std::string>());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -88,25 +105,22 @@ namespace YAML {
|
||||
struct convert<boss::Tag> {
|
||||
static Node encode(const boss::Tag& rhs) {
|
||||
Node node;
|
||||
node["condition"] = rhs.condition;
|
||||
if (!rhs.addTag)
|
||||
node["name"] = "-" + rhs.name;
|
||||
else
|
||||
node["name"] = rhs.name;
|
||||
node["condition"] = rhs.Condition();
|
||||
node["name"] = rhs.PrefixedName();
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, boss::Tag& rhs) {
|
||||
std::string condition, tag;
|
||||
if(node.IsMap()) {
|
||||
std::string condition, tag;
|
||||
if (node["condition"])
|
||||
condition = node["condition"].as<std::string>();
|
||||
if (node["name"])
|
||||
tag = node["name"].as<std::string>();
|
||||
rhs = boss::Tag(tag, condition);
|
||||
} else if (node.IsScalar()) {
|
||||
tag = node.as<std::string>();
|
||||
rhs = boss::Tag(node.as<std::string>());
|
||||
}
|
||||
rhs = boss::Tag(condition, tag);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -138,14 +152,14 @@ namespace YAML {
|
||||
struct convert<boss::Plugin> {
|
||||
static Node encode(const boss::Plugin& rhs) {
|
||||
Node node;
|
||||
node["name"] = rhs.name;
|
||||
node["enabled"] = rhs.enabled;
|
||||
node["priority"] = rhs.priority;
|
||||
node["after"] = rhs.loadAfter;
|
||||
node["req"] = rhs.requirements;
|
||||
node["inc"] = rhs.incompatibilities;
|
||||
node["msg"] = rhs.messages;
|
||||
node["tag"] = rhs.tags;
|
||||
node["name"] = rhs.Name();
|
||||
node["enabled"] = rhs.Enabled();
|
||||
node["priority"] = rhs.Priority();
|
||||
node["after"] = rhs.LoadAfter();
|
||||
node["req"] = rhs.Reqs();
|
||||
node["inc"] = rhs.Incs();
|
||||
node["msg"] = rhs.Messages();
|
||||
node["tag"] = rhs.Tags();
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -155,27 +169,23 @@ namespace YAML {
|
||||
return false;
|
||||
|
||||
if (node["name"])
|
||||
rhs.name = node["name"].as<std::string>();
|
||||
rhs = boss::Plugin(node["name"].as<std::string>());
|
||||
if (node["enabled"])
|
||||
rhs.enabled = node["enabled"].as<bool>();
|
||||
else
|
||||
rhs.enabled = true;
|
||||
rhs.Enabled(node["enabled"].as<bool>());
|
||||
|
||||
if (node["priority"])
|
||||
rhs.priority = node["priority"].as<int>();
|
||||
else
|
||||
rhs.priority = 0;
|
||||
rhs.Priority(node["priority"].as<int>());
|
||||
|
||||
if (node["after"])
|
||||
rhs.loadAfter = node["after"].as< std::list<boss::File> >();
|
||||
rhs.LoadAfter(node["after"].as< std::list<boss::File> >());
|
||||
if (node["req"])
|
||||
rhs.requirements = node["req"].as< std::list<boss::File> >();
|
||||
rhs.Reqs(node["req"].as< std::list<boss::File> >());
|
||||
if (node["inc"])
|
||||
rhs.incompatibilities = node["inc"].as< std::set<boss::File, boss::file_comp> >();
|
||||
rhs.Incs(node["inc"].as< std::set<boss::File, boss::file_comp> >());
|
||||
if (node["msg"])
|
||||
rhs.messages = node["msg"].as< std::list<boss::Message> >();
|
||||
rhs.Messages(node["msg"].as< std::list<boss::Message> >());
|
||||
if (node["tag"])
|
||||
rhs.tags = node["tag"].as< std::list<boss::Tag> >();
|
||||
rhs.Tags(node["tag"].as< std::list<boss::Tag> >());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -195,72 +205,72 @@ namespace YAML {
|
||||
|
||||
Emitter& operator << (Emitter& out, const boss::Message& rhs) {
|
||||
out << BeginMap
|
||||
<< Key << "type" << rhs.type
|
||||
<< Key << "content" << rhs.content;
|
||||
<< Key << "type" << rhs.Type()
|
||||
<< Key << "content" << rhs.Content();
|
||||
|
||||
if (!rhs.language.empty())
|
||||
out << Key << "lang" << rhs.language;
|
||||
if (!rhs.Language().empty())
|
||||
out << Key << "lang" << rhs.Language();
|
||||
|
||||
if (!rhs.condition.empty())
|
||||
out << Key << "condition" << rhs.condition;
|
||||
if (!rhs.Condition().empty())
|
||||
out << Key << "condition" << rhs.Condition();
|
||||
|
||||
out << EndMap;
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const boss::File& rhs) {
|
||||
if (rhs.condition.empty() && rhs.display.empty())
|
||||
out << rhs.name;
|
||||
if (!rhs.IsConditional() && rhs.DisplayName().empty())
|
||||
out << rhs.Name();
|
||||
else {
|
||||
out << BeginMap
|
||||
<< Key << "name" << rhs.name;
|
||||
<< Key << "name" << rhs.Name();
|
||||
|
||||
if (!rhs.condition.empty())
|
||||
out << Key << "condition" << rhs.condition;
|
||||
if (!rhs.Condition().empty())
|
||||
out << Key << "condition" << rhs.Condition();
|
||||
|
||||
if (!rhs.display.empty())
|
||||
out << Key << "display" << rhs.display;
|
||||
if (!rhs.DisplayName().empty())
|
||||
out << Key << "display" << rhs.DisplayName();
|
||||
|
||||
out << EndMap;
|
||||
}
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const boss::Tag& rhs) {
|
||||
if (rhs.condition.empty())
|
||||
out << rhs.Data();
|
||||
if (!rhs.IsConditional())
|
||||
out << rhs.PrefixedName();
|
||||
else {
|
||||
out << BeginMap
|
||||
<< Key << "name" << rhs.Data()
|
||||
<< Key << "condition" << rhs.condition
|
||||
<< Key << "name" << rhs.PrefixedName()
|
||||
<< Key << "condition" << rhs.Condition()
|
||||
<< EndMap;
|
||||
}
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const boss::Plugin& rhs) {
|
||||
if (!rhs.NameOnly()) {
|
||||
if (!rhs.HasNameOnly()) {
|
||||
|
||||
out << BeginMap
|
||||
<< Key << "name" << Value << rhs.name;
|
||||
<< Key << "name" << Value << rhs.Name();
|
||||
|
||||
if (rhs.priority != 0)
|
||||
out << Key << "priority" << Value << rhs.priority;
|
||||
if (rhs.Priority() != 0)
|
||||
out << Key << "priority" << Value << rhs.Priority();
|
||||
|
||||
if (!rhs.enabled)
|
||||
out << Key << "enabled" << Value << rhs.enabled;
|
||||
if (!rhs.Enabled())
|
||||
out << Key << "enabled" << Value << rhs.Enabled();
|
||||
|
||||
if (!rhs.loadAfter.empty())
|
||||
out << Key << "after" << Value << rhs.loadAfter;
|
||||
if (!rhs.LoadAfter().empty())
|
||||
out << Key << "after" << Value << rhs.LoadAfter();
|
||||
|
||||
if (!rhs.requirements.empty())
|
||||
out << Key << "req" << Value << rhs.requirements;
|
||||
if (!rhs.Reqs().empty())
|
||||
out << Key << "req" << Value << rhs.Reqs();
|
||||
|
||||
if (!rhs.incompatibilities.empty())
|
||||
out << Key << "inc" << Value << rhs.incompatibilities;
|
||||
if (!rhs.Incs().empty())
|
||||
out << Key << "inc" << Value << rhs.Incs();
|
||||
|
||||
if (!rhs.messages.empty())
|
||||
out << Key << "msg" << Value << rhs.messages;
|
||||
if (!rhs.Messages().empty())
|
||||
out << Key << "msg" << Value << rhs.Messages();
|
||||
|
||||
if (!rhs.tags.empty())
|
||||
out << Key << "tag" << Value << rhs.tags;
|
||||
if (!rhs.Tags().empty())
|
||||
out << Key << "tag" << Value << rhs.Tags();
|
||||
|
||||
out << EndMap;
|
||||
}
|
||||
@@ -310,5 +320,65 @@ namespace boss {
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace unicode = boost::spirit::unicode;
|
||||
|
||||
template<typename Iterator, typename Skipper>
|
||||
class condition_grammar : public qi::grammar<Iterator, bool(), Skipper> {
|
||||
public:
|
||||
condition_grammar() : condition_grammar::base_type(expression, "condition grammar") {
|
||||
|
||||
expression =
|
||||
condition;
|
||||
|
||||
condition =
|
||||
( qi::lit("if") >> type ) [qi::labels::_val = qi::labels::_1]
|
||||
| ( qi::lit("ifnot") >> type ) [qi::labels::_val = !qi::labels::_1]
|
||||
;
|
||||
|
||||
type =
|
||||
( "file(" > quotedStr > ')' ) [qi::labels::_val = true]
|
||||
| ( "checksum(" > quotedStr > ',' > +unicode::xdigit > ')' ) [qi::labels::_val = true]
|
||||
| ( "version(" > quotedStr > ',' > quotedStr > ',' > unicode::char_ > ')' ) [qi::labels::_val = true]
|
||||
| ( "active(" > quotedStr > ')' ) [qi::labels::_val = true]
|
||||
;
|
||||
|
||||
quotedStr = '"' > +(unicode::char_ - '"') > '"';
|
||||
|
||||
/* Need to add error handlers and actual type evaluation, then start on
|
||||
compound conditional parsing. Need game path support. */
|
||||
}
|
||||
|
||||
private:
|
||||
qi::rule<Iterator, bool(), Skipper> expression, condition, type;
|
||||
qi::rule<Iterator, std::string()> quotedStr;
|
||||
|
||||
|
||||
|
||||
//Eval's regex and exact paths.
|
||||
bool FileExists(bool& result, std::string file);
|
||||
|
||||
|
||||
};
|
||||
|
||||
bool ConditionalData::EvalCondition(const boost::filesystem::path& gamePath) const {
|
||||
if (condition.empty())
|
||||
return true;
|
||||
condition_grammar<std::string::const_iterator, qi::space_type> grammar;
|
||||
qi::space_type skipper;
|
||||
std::string::const_iterator begin, end;
|
||||
bool eval;
|
||||
|
||||
begin = condition.begin();
|
||||
end = condition.end();
|
||||
|
||||
bool r = qi::phrase_parse(begin, end, grammar, skipper, eval);
|
||||
|
||||
if (!r || begin != end)
|
||||
throw std::runtime_error("Parsing of condition \"" + condition + "\" failed!");
|
||||
|
||||
return eval;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/* BOSS
|
||||
|
||||
A plugin load order optimiser for games that use the esp/esm plugin system.
|
||||
|
||||
Copyright (C) 2012 WrinklyNinja
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "plugindata.h"
|
||||
|
||||
#include <vector>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <new>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace boss {
|
||||
|
||||
PluginData::PluginData() : crc(0) {}
|
||||
|
||||
PluginData::PluginData(std::string filename) : name(filename) {
|
||||
//Load by scanning plugin file using libespm.
|
||||
}
|
||||
|
||||
PluginData::PluginData(std::string filename, uint32_t fileCRC) : name(filename), crc(fileCRC) {
|
||||
//Load by scanning cache file.
|
||||
std::string file = name[0] + '/' + name[1] + '/' + name + ".dc";
|
||||
//Lowercase the path.
|
||||
std::for_each(file.begin(), file.end(), tolower);
|
||||
|
||||
if (!boost::filesystem::exists(file))
|
||||
return;
|
||||
|
||||
ifstream ifile(file.c_str(), ios_base::binary);
|
||||
ifile.exceptions(ifstream::failbit | ifstream::badbit | ifstream::eofbit);
|
||||
|
||||
//Get the number of entries.
|
||||
uint32_t count;
|
||||
ifile.seekg(4, ios_base::end);
|
||||
ifile.read((char*)&count, 4);
|
||||
|
||||
//Read index.
|
||||
CacheIndexEntry * index;
|
||||
try {
|
||||
index = new CacheIndexEntry[count];
|
||||
} catch (std::bad_alloc& e) {
|
||||
return;
|
||||
}
|
||||
ifile.seekg(4 + count * sizeof(CacheIndexEntry), ios_base::end);
|
||||
ifile.read((char*)index, count * sizeof(CacheIndexEntry));
|
||||
|
||||
//Search index.
|
||||
uint32_t i = 0;
|
||||
for (i; i < count; i++) {
|
||||
if (index[i].crc == crc)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == count)
|
||||
return;
|
||||
|
||||
//Read plugin data size.
|
||||
uint32_t dataSize;
|
||||
ifile.seekg(index[i].offset, ios_base::beg);
|
||||
ifile.read((char*)&dataSize, 4);
|
||||
|
||||
//Read plugin data.
|
||||
char * data;
|
||||
try {
|
||||
data = new char[dataSize];
|
||||
} catch (std::bad_alloc& e) {
|
||||
return;
|
||||
}
|
||||
ifile.read(data, dataSize);
|
||||
|
||||
//Now we need to read the list of masters.
|
||||
uint16_t mastersLen = *(uint16_t*)data;
|
||||
std::string master;
|
||||
std::vector<std::string> masters;
|
||||
for (int i=2; i < mastersLen; i++) {
|
||||
if (data[i] != NULL)
|
||||
master += data[i];
|
||||
else {
|
||||
masters.push_back(master);
|
||||
master.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//Now read the FormIDs, building up FormID objects from them and their
|
||||
//associated masters.
|
||||
for (int i=2+mastersLen; i < dataSize; i += 4) {
|
||||
int masterID = data[i+3]; //FormIDs are encoded low to high byte.
|
||||
FormID fid;
|
||||
fid.plugin = masters[masterID];
|
||||
fid.objectIndex = *(uint32_t*)data & 0xFFFFFF; //Zero the masterID.
|
||||
formIDs.insert(fid);
|
||||
}
|
||||
|
||||
ifile.close();
|
||||
}
|
||||
|
||||
PluginData::Save() const {
|
||||
//Save to a cache file.
|
||||
std::string outPath = name[0] + '/' + name[1] + '/' + name + ".dc";
|
||||
//Lowercase the path.
|
||||
std::for_each(outPath.begin(), outPath.end(), tolower);
|
||||
|
||||
uint32_t count = 0;
|
||||
CacheIndexEntry * index = NULL;
|
||||
if (boost::filesystem::exists(file)) {
|
||||
ifstream ifile(outPath.c_str(), ios_base::binary);
|
||||
ifile.exceptions(ifstream::failbit | ifstream::badbit | ifstream::eofbit);
|
||||
|
||||
//Get the number of entries.
|
||||
ifile.seekg(4, ios_base::end);
|
||||
ifile.read((char*)&count, 4);
|
||||
|
||||
//Read index.
|
||||
try {
|
||||
index = new CacheIndexEntry[count];
|
||||
} catch (std::bad_alloc& e) {
|
||||
return;
|
||||
}
|
||||
ifile.seekg(4 + count * sizeof(CacheIndexEntry), ios_base::end);
|
||||
ifile.read((char*)&index, count * sizeof(CacheIndexEntry));
|
||||
|
||||
ifile.close();
|
||||
}
|
||||
|
||||
/*Split the FormID object list into a FormID number list and a master list.
|
||||
This doesn't retain the same ordering of masters as the original data,
|
||||
but that doesn't matter. */
|
||||
std::map<std::string, int> masters;
|
||||
std::list<uint32_t> fids;
|
||||
std::string mastersList;
|
||||
int id = 0;
|
||||
for (std::list<FormID>::iterator it = formIDs.begin(), endIt = formIDs.end(); it != endIt; ++it) {
|
||||
std::map<std::string, int>::iterator mit = masters.find(it->plugin);
|
||||
if (mit != masters.end())
|
||||
fids.insert(it->objectIndex | (mit->second<<24));
|
||||
else {
|
||||
id++;
|
||||
masters.insert(pair<std::string, int>(it->plugin, id);
|
||||
fids.insert(it->objectIndex | (id<<24));
|
||||
mastersList += it->plugin + '\0';
|
||||
}
|
||||
}
|
||||
|
||||
ofstream ofile(file.c_str(), ios_base::binary);
|
||||
ofile.exceptions(ifstream::failbit | ifstream::badbit | ifstream::eofbit);
|
||||
|
||||
CacheIndexEntry entry;
|
||||
entry.crc = crc;
|
||||
|
||||
if (!boost::filesystem::exists(file)) {
|
||||
uint8_t nameLength = name.length();
|
||||
ofile.write((char*)&nameLenth, 1);
|
||||
ofile.write((char*)name.data(), nameLength);
|
||||
entry.offset = tellg();
|
||||
} else {
|
||||
ofile.seekg(4 + count * sizeof(CacheIndexEntry), ios_base::end);
|
||||
entry.offset = ofile.tellg();
|
||||
}
|
||||
|
||||
//Write out data.
|
||||
uint16_t mLen = mastersList.size();
|
||||
ofile.write((char*)&mLen, 2);
|
||||
ofile.write(mastersList.data(), mLen);
|
||||
for (std::list<uint32_t>::iterator it = formIDs.begin(), endIt = formIds.end(); it != endIt; ++it) {
|
||||
ofile.write((char*)it, 4);
|
||||
}
|
||||
|
||||
//Write out updated index.
|
||||
if (index != NULL)
|
||||
ofile.write((char*)index, count * sizeof(CacheIndexEntry));
|
||||
ofile.write((char*)&entry, sizeof(CacheIndexEntry));
|
||||
count++;
|
||||
ofile.write((char*)&count, 4);
|
||||
|
||||
ofile.close();
|
||||
}
|
||||
|
||||
PluginData::Empty() const {
|
||||
return formIDs.empty();
|
||||
}
|
||||
|
||||
int PluginData::Overlap(const PluginData& otherPlugin) const {
|
||||
int count = 0;
|
||||
for (std::list<FormID>::iterator it1 = formIDs.begin(), endIt1 = formIDs.end(); it1 != endIt1; ++it1) {
|
||||
for (std::list<FormID>::iterator it2 = otherPlugin.formIDs.begin(), endIt2 = otherPlugin.formIDs.end(); it2 != endIt2; ++it2) {
|
||||
if (*it1 == *it2)
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/* BOSS
|
||||
|
||||
A plugin load order optimiser for games that use the esp/esm plugin system.
|
||||
|
||||
Copyright (C) 2012 WrinklyNinja
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __BOSS_PLUGINDATA_H__
|
||||
#define __BOSS_PLUGINDATA_H__
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
//The functions below make no distinctions between different games. Once a game
|
||||
//handler is implemented, they will be updated to use it.
|
||||
|
||||
namespace boss {
|
||||
|
||||
struct CacheIndexEntry {
|
||||
uint32_t crc;
|
||||
uint32_t offset;
|
||||
}
|
||||
|
||||
struct FormID {
|
||||
std::string plugin;
|
||||
uint32_t objectIndex;
|
||||
}
|
||||
|
||||
struct PluginData {
|
||||
std::string name;
|
||||
uint32_t crc;
|
||||
std::list<FormID> formIDs; // Only the edited FormIDs though.
|
||||
|
||||
PluginData();
|
||||
PluginData(std::string pluginPath);
|
||||
|
||||
/* Can throw std::bad_alloc or std::ios::failure */
|
||||
PluginData(std::string name, uint32_t crc);
|
||||
|
||||
/* Can throw std::bad_alloc or std::ios::failure */
|
||||
void Save() const;
|
||||
bool Empty() const;
|
||||
|
||||
// Returns the record overlap of two PluginData objects.
|
||||
int Overlap(const PluginData& otherPlugin) const;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/* BOSS
|
||||
|
||||
A plugin load order optimiser for games that use the esp/esm plugin system.
|
||||
|
||||
Copyright (C) 2012 WrinklyNinja
|
||||
|
||||
This file is part of BOSS.
|
||||
|
||||
BOSS 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.
|
||||
|
||||
BOSS 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
|
||||
along with BOSS. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __BOSS_SORTING_H__
|
||||
#define __BOSS_SORTING_H__
|
||||
|
||||
#include "plugindata.h"
|
||||
|
||||
namespace boss {
|
||||
|
||||
// None of these comparisons care about plugin name or CRC.
|
||||
inline bool operator==(const PluginData& lhs, const PluginData& rhs){
|
||||
return lhs.formIDs == rhs.formIDs;
|
||||
}
|
||||
|
||||
|
||||
inline bool operator!=(const PluginData& lhs, const PluginData& rhs){return !operator==(lhs,rhs);}
|
||||
|
||||
|
||||
inline bool operator< (const PluginData& lhs, const PluginData& rhs){
|
||||
return lhs.formIDs.size() < rhs.formIDs.size();
|
||||
}
|
||||
|
||||
|
||||
inline bool operator> (const PluginData& lhs, const PluginData& rhs){return operator< (rhs,lhs);}
|
||||
inline bool operator<=(const PluginData& lhs, const PluginData& rhs){return !operator> (lhs,rhs);}
|
||||
inline bool operator>=(const PluginData& lhs, const PluginData& rhs){return !operator< (lhs,rhs);}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+2
-7
@@ -12,13 +12,8 @@ using namespace std;
|
||||
void minimisePluginList(set<boss::Plugin, boss::plugin_comp>& plugins) {
|
||||
set<boss::Plugin, boss::plugin_comp> out;
|
||||
for (set<boss::Plugin, boss::plugin_comp>::iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) {
|
||||
boss::Plugin p = *it;
|
||||
p.priority = 0;
|
||||
p.enabled = true;
|
||||
p.loadAfter.clear();
|
||||
p.requirements.clear();
|
||||
p.incompatibilities.clear();
|
||||
p.messages.clear();
|
||||
boss::Plugin p(it->Name());
|
||||
p.Tags(it->Tags());
|
||||
out.insert(p);
|
||||
}
|
||||
plugins.swap(out);
|
||||
|
||||
Reference in New Issue
Block a user