diff --git a/CMakeLists.txt b/CMakeLists.txt
index 696adef3..a456298c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,7 +11,7 @@
cmake_minimum_required (VERSION 2.8.9)
project (boss)
-set (BOSS_SRC "${CMAKE_SOURCE_DIR}/src/metadata.cpp" "${CMAKE_SOURCE_DIR}/src/game.cpp" "${CMAKE_SOURCE_DIR}/src/helpers.cpp")
+set (BOSS_SRC "${CMAKE_SOURCE_DIR}/src/metadata.cpp" "${CMAKE_SOURCE_DIR}/src/game.cpp" "${CMAKE_SOURCE_DIR}/src/helpers.cpp" "${CMAKE_SOURCE_DIR}/src/plugin/ModFormat.cpp" "${CMAKE_SOURCE_DIR}/src/plugin/VersionRegex.cpp")
# Include source and library directories.
include_directories ("${BOSS_LIBS_DIR}/alphanum" "${BOSS_LIBS_DIR}/utf8" "${BOSS_LIBS_DIR}/boost" "${BOSS_LIBS_DIR}/yaml-cpp/include" "${CMAKE_SOURCE_DIR}/src")
diff --git a/README b/README
index 13a294c9..2c5cd8b4 100644
--- a/README
+++ b/README
@@ -175,9 +175,8 @@ possible, for example:
* Boost: for filesystem interaction, various helpful functions.
As a reflection of this, LOOT's API will provide functions only for
-accessing LOOT-specific functionality and data, eg. auto-sorting,
-masterlist / userlist reading, masterlist download and user
-edits submission.
+accessing LOOT-specific functionality and data, eg. masterlist / userlist reading,
+masterlist download and user edits submission.
The support for arbitrary data sources will be implemented by having the source
LOOT uses specified in a settings file, which will be editable via the GUI.
@@ -529,7 +528,6 @@ bUseOnlineCache Boolean Cache = Database
bGetMetadataUpdates Boolean Metadata = Masterlist
bPromptMetadataSubmit Boolean
bAlwaysSubmitMetadata Boolean Only has effect if the above is false.
-bDoStartupUpdateCheck Boolean Check for program updates.
sOnlineCacheURL String URL to the base of the database repository.
sMetadataURL String
diff --git a/examples/masterlist.yaml b/examples/masterlist.yaml
new file mode 100644
index 00000000..8288a3e7
--- /dev/null
+++ b/examples/masterlist.yaml
@@ -0,0 +1,313 @@
+# This is the Oblivion masterlist up to the end of the ESMs group, converted by
+# hand to MF3 (ie. YAML-based format for BOSSv3). Mods without messages attached
+# have been removed (since they wouldn't be included unless extra data was
+# required for BOSS to sort them.
+
+globals:
+
+ - condition: IF VERSION("../BOSS/BOSS.exe", "2.1.1.0", <)
+ type: warn
+ content: An update for BOSS (v2.1.1) has been released. Download it at "http://code.google.com/p/better-oblivion-sorting-software/downloads/list"
+
+ - condition: IFNOT VERSION("../BOSS/BOSS.exe", "2.1.1.0", <)
+ type: say
+ content: You are using the latest version of BOSS.
+
+plugins:
+
+ - name: Oblivion_1.1.esm
+ msg:
+ - &modESM
+ type: say
+ content: A modding esm - do not use in game, deactivate.
+
+ - name: Oblivion_SI.esm
+ msg:
+ - *modESM
+
+ - name: Oblivion_GOTY non-SI.esm
+ msg:
+ - *modESM
+
+ - name: Hammerfell.esm
+ msg:
+ - condition: if checksum("Hammerfell.esm", 7D22F9DF)
+ type: warn
+ content: 'Contains dirty edits; 0 ITM, 4 UDR records. Needs TES4Edit cleaning: "http://cs.elderscrolls.com/constwiki/index.php/TES4Edit_Cleaning_Guide"'
+
+ - name: GTAesgaard_2.esm
+ msg:
+ - &warnWorldspaces
+ type: warn
+ content: This needs to load directly after Oblivion.esm to avoid landscape issues caused by a failure by the mod author to use TES4Gecko's "Move Worldspaces" function.
+
+ - name: DibellasWatch.esm
+ msg:
+ - *warnWorldspaces
+
+ - name: Belas.esm
+ msg:
+ - type: warn
+ content: Part of Chronicles of Reeb. Do not use. Has serious landscape issues due to editing the Tamriel worldspace. Uninstall and delete.
+
+ - name: Morrowind_ob.esm
+ msg:
+ - *warnWorldspaces
+
+ - name: Silgrad_Tower.esm
+ msg:
+ - condition: if file("Silgrad_Tower_T.esm")
+ type: warn
+ content: Use either Silgrad_Tower.esm or Silgrad_Tower_T.esm but not both together.
+
+ - name: Silgrad_Tower_T.esm
+ msg:
+ - condition: if file("Silgrad_Tower.esm")
+ type: warn
+ content: Use either Silgrad_Tower.esm or Silgrad_Tower_T.esm but not both together.
+
+ - name: Beautiful People 2ch-Ed.esm
+ tag:
+ - Eyes
+ - Graphics
+ - Hair
+ - R.Relations
+
+ - name: xeoex.esm
+ tag:
+ - Body-F
+ - Body-Size-F
+ - Body-M
+ - Body-Size-M
+ - Eyes
+
+ - name: EnhancedWeatherSIOnly.esm
+ msg:
+ - type: warn
+ content: Obsolete. Remove this and install Enhanced Weather.
+
+ - name: Francesco's Leveled Creatures-Items Mod.esm
+ tag:
+ - Actors.ACBS
+ - Actors.AIData
+ - Actors.AIPackages
+ - Actors.CombatStype
+ - Actors.DeathItem
+ - Actors.Stats
+ - Delev
+ - Invent
+ - NpcFaces
+ - Relev
+ - Scripts
+ - Stats
+ msg:
+ - condition: IF CHECKSUM("Francesco's Leveled Creatures-Items Mod.esm", 17F6FD1A)
+ type: warn
+ content: 'Contains dirty edits; 6 ITM, 0 UDR records. Needs TES4Edit cleaning: "http://cs.elderscrolls.com/constwiki/index.php/TES4Edit_Cleaning_Guide"'
+
+ - name: Francesco's Optional New Items Add-On.esm
+ tag:
+ - Delev
+ - Relev
+ msg:
+ - condition: IF CHECKSUM("Francesco's Optional New Items Add-On.esm", DFD4FB97)
+ type: warn
+ content: 'Contains dirty edits; 2 ITM, 0 UDR records. Needs TES4Edit cleaning: "http://cs.elderscrolls.com/constwiki/index.php/TES4Edit_Cleaning_Guide"'
+
+ - name: Francesco's Optional New Creatures Add-On.esm
+ tag:
+ - Actors.AIPackages
+ - Delev
+ - Factions
+ - Invent
+ - Relations
+ - Relev
+ - Scripts
+ msg:
+ - condition: &fcomCond IF FILE("FCOM_Convergence.esm")
+ type: error
+ content: Delete. Not compatible with FCOM. Already integrated into FCOM.
+
+ - name: Crafting Skill Engine.esm
+ msg:
+ - condition: IF FILE("Retros Fletching and Imbueing 1.7.1_rus.esp")
+ type: error
+ content: Delete. Included in Retros Fletching and Imbueing 1.7.1_rus.esp
+
+ - name: Tamrielic Ingredients.esm
+ msg:
+ - type: say
+ content: Do not use Tamrielic Ingredients and COBL at the same time.
+
+ - name: Cobl Main.esm
+ msg:
+ - type: say
+ content: Do not use Tamrielic Ingredients and COBL at the same time.
+
+ - name: Item interchange.esm
+ msg:
+ - type: say
+ content: Outdated. All functions are now handled by Cobl.esm. Update to the latest versions of 'Item Interchange' and Cobl.
+
+ - name: Qarls_Harvest.esm
+ tag:
+ - Scripts
+
+ - name: DEJ Harvest - Flora.esm
+ msg:
+ - type: say
+ content: Obsolete. Update to 'Harvest [Flora]' v3.0.1+.
+
+ - name: Tamriel's Glittering Geology.esm
+ msg:
+ - condition: &oooCond IF FILE("Oscuro's_Oblivion_Overhaul.esm")
+ type: error
+ content: Not compatible with OOO. Already included in OOO.
+
+ - name: Oscuro's_Oblivion_Overhaul.esm
+ tag:
+ - Actors.Spells
+ - Graphics
+ - Invent
+ - Relations
+ - Scripts
+ - Stats
+ - condition: IF FILE("Mart's Monster Mod for OOO.esm") || IF VAR(FCOM)
+ name: -Relations
+ msg:
+ - type: say
+ content: Do not clean. "Dirty" edits are intentional and required for the mod to function.
+
+ - name: Mart's Monster Mod.esm
+ tag:
+ - Actors.AIData
+ - Actors.AIPackages
+ - Actors.Spells
+ - Actors.Stats
+ - Delev
+ - Factions
+ - Graphics
+ - Invent
+ - Names
+ - Relations
+ - Relev
+ - Scripts
+ - Stats
+
+ - name: Mart's Monster Mod for OOO.esm
+ msg:
+ - condition: *fcomCond
+ type: error
+ content: Delete. Not compatible with FCOM.
+ tag:
+ - Delev
+ - Factions
+ - Relations
+ - Relev
+
+# The message attached to the Waalx plugin below is too vague for the specialised
+# incompatibility list in MF3, and should probably be removed.
+ - name: Waalx Animals & Creatures.esm
+ msg:
+ - type: say
+ content: Incompatible with other overhaul mods.
+
+ - name: FCOM_Convergence.esm
+ tag:
+ - Delev
+ - Factions
+ - Relations
+ - Relev
+
+ - name: Armamentarium.esm
+ tag:
+ - Relev
+
+ - name: ArmamentariumFran.esm
+ tag:
+ - Relev
+
+ - name: ArmamentariumEnchanted.esm
+ msg:
+ - type: say
+ content: Outdated, included in Armamentarium Complete.
+
+ - name: bookplacing.esm
+ msg:
+ - type: warn
+ content: Check you are using v2+. If not, Update. v1 has a severe bug with the Mystic Emporium disappearing.
+
+ - name: No Lights Flicker.esm
+ msg:
+ - condition: &bpTweakMsg IF FILE("Bashed Patch.*\.esp")
+ type: say
+ content: Use Wrye Bash Bashed Patch tweak instead.
+
+# If a plugin name was given for Reznod's Mannequins, the incompatibility list
+# syntax could be used.
+ - name: TLS Modded Mannequins.esm
+ msg:
+ - type: say
+ content: Incompatible with Reznod's Mannequins.
+
+ - name: DKImperials.esm
+ tag:
+ - Hair
+ - Eyes
+
+ - name: Minotaur Race.esm
+ req:
+ - name: &OBSE ../obse_loader.exe
+ condition: if version("../obse_loader.exe", "0.0.18.0", >=)
+ display: OBSE v18+
+ msg:
+ - type: say
+ content: Requires 'Race Repository'
+ - condition: IF CHECKSUM("Minotaur Race.esm", 477340C8)
+ type: warn
+ content: 'Contains dirty edits; 7 ITM, 0 UDR records. Needs TES4Edit cleaning: "http://cs.elderscrolls.com/constwiki/index.php/TES4Edit_Cleaning_Guide"'
+
+ - name: x117race.esm
+ tag:
+ - Hair
+
+ - name: Chocolate Elves.esm
+ tag:
+ - NpcFaces
+ - Hair
+ - Eyes
+ - R.Relations
+
+ - name: Independent High Elf Body (Replace)(v1.1).esm
+ tag:
+ - NpcFaces
+ - Hair
+ - Eyes
+ - R.Relations
+
+ - name: p2p'sAshDraconian.esm
+ tag:
+ - NpcFaces
+ - Hair
+ - Eyes
+ - R.Relations
+
+ - name: SPTDiverseGuards-Resources.esm
+ msg:
+ - condition: *oooCond
+ type: say
+ content: Probably not compatible with OOO. Untested.
+
+ - name: John's Leveled List Overhaul.esm
+ tag:
+ - Graphics
+
+# I've just made up the OBME DLL name and version here for an example.
+ - name: Diverse Magic Effects Resource.esm
+ req:
+ - name: *OBSE
+ condition: if version("../obse_loader.exe", "0.0.19.0", >=)
+ display: OBSE v19+
+ - name: OBSE/Plugins/obme.dll
+ condition: if version("../obse_loader.exe", "0.1.0.0", >=)
+ display: OBME v1+
diff --git a/examples/settings.yaml b/examples/settings.yaml
new file mode 100644
index 00000000..88da0e7a
--- /dev/null
+++ b/examples/settings.yaml
@@ -0,0 +1,20 @@
+# BOSS Settings File
+# This file is written in YAML .
+# If you don't know YAML, just think of a normal ini file, but replace the
+# equals signs with colons followed by a space. No tabs allowed.
+
+bEnableNetworking: true
+bUseOnlineCache: true
+bGetMetadataUpdates: true
+bPromptMetadataSubmit: true
+bAlwaysSubmitMetadata: true
+
+sOnlineCacheURL: "http://better-oblivion-sorting-software.googlecode.com/svn/data/boss-nehrim/masterlist.txt"
+sMetadataURL: "http://better-oblivion-sorting-software.googlecode.com/svn/data/boss-nehrim/masterlist.txt"
+sVersionCheckerURL: "http://better-oblivion-sorting-software.googlecode.com/svn/data/boss-nehrim/masterlist.txt"
+
+sGame: auto # auto, oblivion, skyrim, fallout3, falloutnv
+sLastGame: auto # auto, oblivion, skyrim, fallout3, falloutnv
+iDebugVerbosity: 0 # 0, 1, 2, 3. Logging takes place if > 0.
+bDoTrialRun: false
+sLanguage: eng # An ISO 639-3 language string.
diff --git a/src/game.cpp b/src/game.cpp
index 3702f235..299257ad 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -27,6 +27,8 @@
#include
+#include
+
#if _WIN32 || _WIN64
# include
# include
@@ -116,6 +118,15 @@ namespace boss {
return GamePath() / pluginsFolderName;
}
+ bool Game::IsActive(const std::string& plugin) const {
+ if (activePlugins.empty()) {
+ //Use libloadorder to fetch the active plugins list. Fill the set
+ //with the lowercased filenames.
+ }
+
+ return activePlugins.find(boost::to_lower_copy(plugin)) != activePlugins.end();
+ }
+
void Game::CreateBOSSGameFolder() {
//Make sure that the BOSS game path exists.
try {
diff --git a/src/game.h b/src/game.h
index 8709561e..ac9e9419 100644
--- a/src/game.h
+++ b/src/game.h
@@ -24,7 +24,12 @@
#ifndef __BOSS_GAME__
#define __BOSS_GAME__
+#include
+#include
+
#include
+#include
+#include
namespace boss {
@@ -44,6 +49,12 @@ namespace boss {
//Creates directory in BOSS folder for BOSS's game-specific files.
void CreateBOSSGameFolder();
+
+ bool IsActive(const std::string& plugin) const;
+
+ //Caches for condition results, active plugins and CRCs.
+ boost::unordered_map conditionCache; //Holds lowercased strings.
+ boost::unordered_map crcCache; //Holds lowercased strings.
private:
uint32_t id;
std::string name;
@@ -55,6 +66,7 @@ namespace boss {
std::string pluginsFolderName;
boost::filesystem::path gamePath; //Path to the game's folder.
+ boost::unordered_set activePlugins; //Holds lowercased strings.
//Can be used to get the location of the LOCALAPPDATA folder (and its Windows XP equivalent).
boost::filesystem::path GetLocalAppDataPath();
diff --git a/src/helpers.cpp b/src/helpers.cpp
index 9cd0b78f..2b88efb5 100644
--- a/src/helpers.cpp
+++ b/src/helpers.cpp
@@ -26,6 +26,7 @@
*/
#include "helpers.h"
+#include "plugin/ModFormat.h"
#include
#include
@@ -78,15 +79,15 @@ namespace boss {
}
//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) {
+ std::string IntToString(const int n) {
string out;
back_insert_iterator sink(out);
- karma::generate(sink,karma::upper[karma::uint_],n);
+ karma::generate(sink,karma::upper[karma::int_],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) {
+ std::string IntToHexString(const int n) {
string out;
back_insert_iterator sink(out);
karma::generate(sink,karma::upper[karma::hex],n);
@@ -156,53 +157,58 @@ namespace boss {
Version::Version(const fs::path file) {
// LOG_TRACE("extracting version from '%s'", file.string().c_str());
+ if (file.extension().string() == ".esm" || file.extension().string() == ".esp") {
+ //Use libespm interface.
+ verString = ReadHeader(file).Version;
+ } else {
#if _WIN32 || _WIN64
- DWORD dummy = 0;
- DWORD size = GetFileVersionInfoSize(file.wstring().c_str(), &dummy);
+ 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;
+ if (size > 0) {
+ LPBYTE point = new BYTE[size];
+ UINT uLen;
+ VS_FIXEDFILEINFO *info;
+ string ver;
- GetFileVersionInfo(file.wstring().c_str(),0,size,point);
+ GetFileVersionInfo(file.wstring().c_str(),0,size,point);
- VerQueryValue(point,L"\\",(LPVOID *)&info,&uLen);
+ 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);
+ DWORD dwLeftMost = HIWORD(info->dwFileVersionMS);
+ DWORD dwSecondLeft = LOWORD(info->dwFileVersionMS);
+ DWORD dwSecondRight = HIWORD(info->dwFileVersionLS);
+ DWORD dwRightMost = LOWORD(info->dwFileVersionLS);
- delete [] point;
+ delete [] point;
- verString = IntToString(dwLeftMost) + '.' + IntToString(dwSecondLeft) + '.' + IntToString(dwSecondRight) + '.' + IntToString(dwRightMost);
- }
+ 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/'";
+ // 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");
+ 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());
+ // 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);
}
- 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 {
@@ -254,6 +260,10 @@ namespace boss {
return (*this == ver || *this > ver);
}
+ bool Version::operator <= (Version ver) {
+ return (*this == ver || *this < ver);
+ }
+
bool Version::operator == (Version ver) {
return (verString == ver.AsString());
}
diff --git a/src/helpers.h b/src/helpers.h
index 297bec1b..fa10083d 100644
--- a/src/helpers.h
+++ b/src/helpers.h
@@ -28,6 +28,7 @@
#ifndef __BOSS_HELPERS__
#define __BOSS_HELPERS__
+#include
#include
#include
@@ -42,10 +43,10 @@ namespace boss {
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);
+ std::string IntToString(const int 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);
+ std::string IntToHexString(const int n);
//Converts a boolean to a string representation (true/false)
std::string BoolToString(const bool b);
@@ -71,6 +72,7 @@ namespace boss {
bool operator > (Version);
bool operator < (Version);
bool operator >= (Version);
+ bool operator <= (Version);
bool operator == (Version);
bool operator != (Version);
};
diff --git a/src/metadata.cpp b/src/metadata.cpp
index 88391497..711fe1d7 100644
--- a/src/metadata.cpp
+++ b/src/metadata.cpp
@@ -22,6 +22,7 @@
*/
#include "metadata.h"
+#include
using namespace std;
@@ -188,37 +189,37 @@ namespace boss {
tags = t;
}
- void Plugin::EvalAllConditions(const boost::filesystem::path& gamePath) {
+ void Plugin::EvalAllConditions(boss::Game& game) {
for (list::iterator it = loadAfter.begin(); it != loadAfter.end();) {
- if (!it->EvalCondition(gamePath))
+ if (!it->EvalCondition(game))
it = loadAfter.erase(it);
else
++it;
}
for (list::iterator it = requirements.begin(); it != requirements.end();) {
- if (!it->EvalCondition(gamePath))
+ if (!it->EvalCondition(game))
it = requirements.erase(it);
else
++it;
}
for (set::iterator it = incompatibilities.begin(); it != incompatibilities.end();) {
- if (!it->EvalCondition(gamePath))
+ if (!it->EvalCondition(game))
incompatibilities.erase(it++);
else
++it;
}
for (list::iterator it = messages.begin(); it != messages.end();) {
- if (!it->EvalCondition(gamePath))
+ if (!it->EvalCondition(game))
it = messages.erase(it);
else
++it;
}
for (list::iterator it = tags.begin(); it != tags.end();) {
- if (!it->EvalCondition(gamePath))
+ if (!it->EvalCondition(game))
it = tags.erase(it++);
else
++it;
@@ -230,6 +231,6 @@ namespace boss {
}
bool Plugin::IsRegexPlugin() const {
- return name.substr(name.length()-5) == "\\.esp" || name.substr(name.length()-5) == "\\.esm";
+ return boost::iends_with(name, "\\.esm") || boost::iends_with(name, "\\.esp");
}
}
diff --git a/src/metadata.h b/src/metadata.h
index 06d82cf9..8a17549e 100644
--- a/src/metadata.h
+++ b/src/metadata.h
@@ -23,6 +23,8 @@
#ifndef __BOSS_METADATA__
#define __BOSS_METADATA__
+#include "game.h"
+
#include
#include
#include
@@ -38,7 +40,7 @@ namespace boss {
ConditionalData(const std::string& condition, const std::string& data);
bool IsConditional() const;
- bool EvalCondition(const boost::filesystem::path& gamePath) const;
+ bool EvalCondition(boss::Game& game) const;
std::string Condition() const;
std::string Data() const;
@@ -124,7 +126,7 @@ namespace boss {
void Messages(const std::list& messages);
void Tags(const std::list& tags);
- void EvalAllConditions(const boost::filesystem::path& gamePath);
+ void EvalAllConditions(boss::Game& game);
bool HasNameOnly() const;
bool IsRegexPlugin() const;
private:
diff --git a/src/parsers.h b/src/parsers.h
index 9e88ddb7..70aa555a 100644
--- a/src/parsers.h
+++ b/src/parsers.h
@@ -29,10 +29,14 @@
#endif
#include "metadata.h"
+#include "helpers.h"
#include
+#include
+#include
#include
+#include
#include
#include
#include
@@ -279,50 +283,16 @@ namespace YAML {
namespace boss {
- /* Conditional parser
+ ///////////////////////////////
+ // Condition parser/evaluator
+ ///////////////////////////////
- Expression is of the form:
-
- condition = keyword type
-
- expression = -'(' condition (operator condition)* -')' operator
-
- comp_exp = expression (operator expression)*
- -'(' keyword type (operator -'(' keyword type -')' )*
-
- keyword = if | ifnot
- operator = and | or
- type = file(...) | checksum(...) | version(...) | active(...)
-
- 'and' takes precedence over 'or', and brackets can also be used to indicate
- overriding precedences.
-
- Split string up into sub-strings according to the bracketing if present, then
- according to operators, until each sub-string only contains one condition.
-
- {
- expression =
- term
- >> *(string("or") >> term)
- ;
-
- term =
- factor
- >> *(string("and") >> factor)
-
- factor =
- condition
- | '(' >> expression >> ')'
- |
-
- condition = keyword >> type
-
- }
-
- */
+ /* Still need to add support for bracketing and evaluation of the compound
+ conditions. */
namespace qi = boost::spirit::qi;
namespace unicode = boost::spirit::unicode;
+ namespace phoenix = boost::phoenix;
template
class condition_grammar : public qi::grammar {
@@ -330,7 +300,14 @@ namespace boss {
condition_grammar() : condition_grammar::base_type(expression, "condition grammar") {
expression =
- condition;
+ andStatement
+ >> *(qi::lit("or") >> andStatement)
+ ;
+
+ andStatement =
+ condition
+ >> *(qi::lit("and") >> condition)
+ ;
condition =
( qi::lit("if") >> type ) [qi::labels::_val = qi::labels::_1]
@@ -338,38 +315,144 @@ namespace boss {
;
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]
+ ( "file(" > quotedStr > ')' ) [phoenix::bind(&condition_grammar::CheckFile, this, qi::labels::_val, qi::labels::_1)]
+ | ( "checksum(" > quotedStr > ',' > qi::hex > ')' ) [phoenix::bind(&condition_grammar::CheckSum, this, qi::labels::_val, qi::labels::_1, qi::labels::_2)]
+ | ( "version(" > quotedStr > ',' > quotedStr > ',' > unicode::char_ > ')' ) [phoenix::bind(&condition_grammar::CheckVersion, this, qi::labels::_val, qi::labels::_1, qi::labels::_2, qi::labels::_3)]
+ | ( "active(" > quotedStr > ')' ) [phoenix::bind(&condition_grammar::CheckActive, this, qi::labels::_val, qi::labels::_1)]
;
quotedStr = '"' > +(unicode::char_ - '"') > '"';
- /* Need to add error handlers and actual type evaluation, then start on
- compound conditional parsing. Need game path support. */
+ expression.name("expression");
+ condition.name("condition");
+ type.name("condition type");
+ quotedStr.name("quoted string");
+
+ qi::on_error(expression, phoenix::bind(&condition_grammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
+ qi::on_error(condition, phoenix::bind(&condition_grammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
+ qi::on_error(type, phoenix::bind(&condition_grammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
+ qi::on_error(quotedStr, phoenix::bind(&condition_grammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
+ }
+
+ void SetGame(boss::Game& g) {
+ game = &g;
}
private:
qi::rule expression, condition, type;
qi::rule quotedStr;
+ boss::Game * game;
+
+ //Eval's regex and exact paths. Check for files and ghosted plugins.
+ void CheckFile(bool& result, const std::string& file) {
+ if (boost::contains(file, "\\.")) { //Regex. Only supports filenames right now.
+
+ result = false;
+ boost::regex regex;
+ try {
+ regex = boost::regex(file, boost::regex::extended|boost::regex::icase);
+ } catch (boost::regex_error e) {
+ throw std::runtime_error("The regex string \"" + file + "\" is invalid.");
+ // LOG_ERROR("\"%s\" is not a valid regular expression. Item skipped.", reg.c_str());
+ }
+ for (fs::directory_iterator itr(game->DataPath()); itr != fs::directory_iterator(); ++itr) {
+ if (fs::is_regular_file(itr->status())) {
+ if (boost::regex_match(itr->path().filename().string(), regex)) {
+ result = true;
+ break;
+ }
+ }
+ }
- //Eval's regex and exact paths.
- bool FileExists(bool& result, std::string file);
+ } else if (IsPlugin(file))
+ result = boost::filesystem::exists(game->DataPath() / file) || boost::filesystem::exists(game->DataPath() / (file + ".ghost"));
+ else
+ result = boost::filesystem::exists(game->DataPath() / file);
+ }
+ void CheckSum(bool& result, const std::string& file, const uint32_t checksum) {
+ uint32_t crc;
+ boost::unordered_map::iterator it = game->crcCache.find(boost::to_lower_copy(file));
+ if (it != game->crcCache.end())
+ crc = it->second;
+ else {
+ if (boost::filesystem::exists(game->DataPath() / file))
+ crc = GetCrc32(game->DataPath() / file);
+ else if (IsPlugin(file) && boost::filesystem::exists(game->DataPath() / (file + ".ghost")))
+ crc = GetCrc32(game->DataPath() / (file + ".ghost"));
+ else {
+ result = false;
+ return;
+ }
+
+ game->crcCache.emplace(boost::to_lower_copy(file), crc);
+ }
+
+ result = checksum == crc;
+ }
+
+ void CheckVersion(bool& result, const std::string& file, const std::string& version, const char comparator) {
+
+ CheckFile(result, file);
+ if (!result) {
+ if (comparator == '<')
+ result = true;
+ return;
+ }
+
+ Version givenVersion = Version(version);
+ Version trueVersion = Version(game->DataPath() / file);
+
+ switch (comparator) {
+ case '>':
+ if (trueVersion <= givenVersion)
+ result = false;
+ break;
+ case '<':
+ if (trueVersion >= givenVersion)
+ result = false;
+ break;
+ case '=':
+ if (trueVersion != givenVersion)
+ result = false;
+ break;
+ }
+ }
+
+ void CheckActive(bool& result, const std::string& file) {
+ result = game->IsActive(file);
+ }
+
+ bool IsPlugin(const std::string& file) {
+ return boost::iends_with(file, ".esm") || boost::iends_with(file, ".esp");
+ }
+
+ void SyntaxError(Iterator const& /*first*/, Iterator const& last, Iterator const& errorpos, boost::spirit::info const& what) {
+
+ std::string context(errorpos, min(errorpos +50, last));
+ boost::trim(context);
+
+ throw std::runtime_error("Error parsing condition at \"" + context + "\", expected \"" + what.tag + "\"");
+ }
};
- bool ConditionalData::EvalCondition(const boost::filesystem::path& gamePath) const {
+ bool ConditionalData::EvalCondition(boss::Game& game) const {
if (condition.empty())
return true;
+
+ boost::unordered_map::const_iterator it = game.conditionCache.find(boost::to_lower_copy(condition));
+ if (it != game.conditionCache.end())
+ return it->second;
+
condition_grammar grammar;
qi::space_type skipper;
std::string::const_iterator begin, end;
bool eval;
+ grammar.SetGame(game);
begin = condition.begin();
end = condition.end();
@@ -378,6 +461,8 @@ namespace boss {
if (!r || begin != end)
throw std::runtime_error("Parsing of condition \"" + condition + "\" failed!");
+ game.conditionCache.emplace(boost::to_lower_copy(condition), eval);
+
return eval;
}
}
diff --git a/src/plugin/ModFormat.cpp b/src/plugin/ModFormat.cpp
new file mode 100644
index 00000000..fadbdc66
--- /dev/null
+++ b/src/plugin/ModFormat.cpp
@@ -0,0 +1,241 @@
+/* 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
+ .
+
+ $Revision: 2488 $, $Date: 2011-03-27 14:31:33 +0100 (Sun, 27 Mar 2011) $
+*/
+
+
+#include "Types.h"
+#include "ModFormat.h"
+#include "VersionRegex.h"
+
+#include
+#include
+#include
+#include
+
+namespace boss {
+ using namespace std;
+
+ using boost::algorithm::trim_copy;
+ using boost::regex;
+
+ //
+ // string ParseVersion(string&):
+ // - Tries to extract the version string value from the given text,
+ // using the above defined regexes to do the dirty work.
+ //
+ string ParseVersion(const string& text){
+
+ string::const_iterator begin, end;
+
+ begin = text.begin();
+ end = text.end();
+
+ for(int i = 0; regex* re = version_checks[i]; i++) {
+
+ smatch what;
+ while (regex_search(begin, end, what, *re)) {
+
+ if (what.empty()){
+ continue;
+ }
+
+ ssub_match match = what[1];
+
+ if (!match.matched) {
+ continue;
+ }
+
+ return trim_copy(string(match.first, match.second));
+
+ }
+ }
+
+ return string();
+ }
+
+ //
+ // string ReadString(pointer&, maxsize):
+ // - Reads a consecutive array of charactes up to maxsize length and
+ // returns them as a new string.
+ //
+ string ReadString(char*& bufptr, ushort size){
+ string data;
+
+ data.reserve(size + 1);
+ while (char c = *bufptr++) {
+ data.append(1, c);
+ }
+
+ return data;
+ }
+
+ //
+ // T Peek(pointer&):
+ // - Peeks into the received buffer and returns the value pointed
+ // converting it to the type T.
+ //
+ template
+ T Peek(char* buffer) {
+ return *reinterpret_cast(buffer);
+ }
+
+ //
+ // T Read(pointer&):
+ // - Tries to extract a value of the specified type T from the
+ // received buffer, incrementing the pointer to point past the readen
+ // value.
+ //
+ template
+ inline T Read(char*& buffer) {
+ T value = Peek(buffer);
+ buffer += sizeof(T);
+ return value;
+ }
+
+ //-
+ // ModHeader ReadHeader(string):
+ // - Parses the mod file contents and extract the header information
+ // returning the most important data using a ModHeader struct.
+ // --> see:
+ // http://www.uesp.net/wiki/Tes4Mod:Mod_File_Format,
+ //
+ // and in particular this link:
+ // http://www.uesp.net/wiki/Tes4Mod:Mod_File_Format/TES4
+ //
+
+ bool IsPluginMaster(boost::filesystem::path filename) {
+ char buffer[MAXLENGTH];
+ char* bufptr = buffer;
+ ModHeader modHeader;
+
+ if (filename.empty())
+ return false;
+
+ ifstream file(filename.native().c_str(), ios_base::binary | ios_base::in);
+
+ if (file.bad())
+ //throw boss_error(BOSS_ERROR_FILE_READ_FAIL, filename.string());
+ return false;
+
+ // Reads the first MAXLENGTH bytes into the buffer
+ file.read(&buffer[0], sizeof(buffer));
+
+ // Check for the 'magic' marker at start
+ if (Read(bufptr) != Record::TES4){
+ return false;
+ }
+
+ // Next field is the total header size
+ /*uint headerSize =*/ Read(bufptr);
+
+ // Next comes the header record Flags
+ uint flags = Read(bufptr);
+
+ // LSb of this record's flags is used to indicate if the
+ // mod is a master or a plugin
+ return ((flags & 0x1) != 0);
+ }
+
+ ModHeader ReadHeader(boost::filesystem::path filename) {
+ char buffer[MAXLENGTH];
+ char* bufptr = buffer;
+ ModHeader modHeader;
+ ifstream file(filename.native().c_str(), ios_base::binary | ios_base::in);
+
+ modHeader.Name = filename.string();
+
+ // Reads the first MAXLENGTH bytes into the buffer
+ file.read(&buffer[0], sizeof(buffer));
+
+ // Check for the 'magic' marker at start
+ if (Read(bufptr) != Record::TES4){
+ return modHeader;
+ }
+
+ // Next field is the total header size
+ /*uint headerSize =*/ Read(bufptr);
+
+ // Next comes the header record Flags
+ uint flags = Read(bufptr);
+
+ // LSb of this record's flags is used to indicate if the
+ // mod is a master or a plugin
+ modHeader.IsMaster = (flags & 0x1) != 0;
+
+ // Next comes the FormID...
+ /*uint formId =*/ Read(bufptr); // skip formID
+
+ // ...and extra flags
+ /*uint flags2 =*/ Read(bufptr); // skip flags2
+
+ // Here the Header record starts, check for its signature 'HEDR'
+ if (Read(bufptr) != Record::HEDR){
+ return modHeader;
+ }
+
+ // HEDR record has fields: DataSize, Version (0.8 o 1.0), Record Count
+ // and Next Object Id
+ /*ushort dataSize =*/ Read(bufptr);
+ /*float version =*/ Read(bufptr);
+ /*int numRecords =*/ Read(bufptr);
+ /*uint nextObjId =*/ Read(bufptr);
+
+ // Then comes the sub-records
+ uint signature = Read(bufptr);
+
+ // skip optional records
+ bool loop = true;
+ while (loop){
+ switch (signature)
+ {
+ case Record::OFST:
+ case Record::DELE:
+ bufptr += Read(bufptr); // skip
+ signature = Read(bufptr);
+ break;
+
+ // extract author name, if present
+ case Record::CNAM:
+ modHeader.Author = ReadString(bufptr, Read(bufptr));
+ signature = Read(bufptr);
+ break;
+
+ // extract description and version, if present
+ case Record::SNAM:
+ modHeader.Description = ReadString(bufptr, Read(bufptr));
+ modHeader.Version = ParseVersion(modHeader.Description);
+ signature = Read(bufptr);
+ break;
+
+ default:
+ loop = false;
+ }
+ }
+
+ // We should have all the required information.
+ return modHeader;
+ }
+}
diff --git a/src/plugin/ModFormat.h b/src/plugin/ModFormat.h
new file mode 100644
index 00000000..54fcf5c4
--- /dev/null
+++ b/src/plugin/ModFormat.h
@@ -0,0 +1,64 @@
+/* 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
+ .
+
+ $Revision: 2488 $, $Date: 2011-03-27 14:31:33 +0100 (Sun, 27 Mar 2011) $
+*/
+
+#ifndef __SUPPORT_MODFORMAT__HPP__
+#define __SUPPORT_MODFORMAT__HPP__
+
+#include "Types.h"
+#include
+#include
+
+
+namespace boss {
+
+ using namespace std;
+
+ // Structure for grouping the information gathered from each mod's header.
+ struct ModHeader {
+ string Name;
+ string Description;
+ string Author;
+ string Version;
+ bool IsMaster;
+ };
+
+
+ struct Record {
+ static const ulong TES4 = '4SET';
+ static const ulong HEDR = 'RDEH';
+ static const ulong OFST = 'TSFO';
+ static const ulong DELE = 'ELED';
+ static const ulong CNAM = 'MANC';
+ static const ulong SNAM = 'MANS';
+ };
+
+ ModHeader ReadHeader(boost::filesystem::path filename);
+
+ bool IsPluginMaster(boost::filesystem::path filename); //Shorter version of the above, to only get master flag.
+}
+
+#endif
diff --git a/src/plugin/Types.h b/src/plugin/Types.h
new file mode 100644
index 00000000..7062cce9
--- /dev/null
+++ b/src/plugin/Types.h
@@ -0,0 +1,51 @@
+/* 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
+ .
+
+ $Revision: 1679 $, $Date: 2010-10-17 20:41:49 +0100 (Sun, 17 Oct 2010) $
+*/
+
+#ifndef __SUPPORT_TYPES__HPP__
+#define __SUPPORT_TYPES__HPP__
+
+
+namespace boss {
+
+ //////////////////////////////////////////////////////////////////////////
+ // Common type definitions
+ //////////////////////////////////////////////////////////////////////////
+
+ typedef unsigned long ulong;
+ typedef unsigned int uint;
+ typedef unsigned short ushort;
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Constants
+ //////////////////////////////////////////////////////////////////////////
+
+ // An arbitrary large number. Controls the size of some buffers used to read data from files.
+ const ulong MAXLENGTH = 4096UL;
+}
+
+#endif
diff --git a/src/plugin/VersionRegex.cpp b/src/plugin/VersionRegex.cpp
new file mode 100644
index 00000000..c5842431
--- /dev/null
+++ b/src/plugin/VersionRegex.cpp
@@ -0,0 +1,92 @@
+/* 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
+ .
+
+ $Revision: 1681 $, $Date: 2010-10-17 21:01:25 +0100 (Sun, 17 Oct 2010) $
+*/
+
+
+#include
+#include
+
+
+namespace boss {
+
+ using namespace std;
+ using namespace boost;
+
+ /// REGEX expression definition
+ /// Each expression is composed of three parts:
+ /// 1. The marker string "version", "ver", "rev", "v" or "r"
+ /// 2. The version string itself.
+
+ const char* regex1 =
+ "^(?:\\bversion\\b[ ]*(?:[:.\\-]?)|\\brevision\\b(?:[:.\\-]?))[ ]*"
+ "((?:alpha|beta|test|debug)?\\s*[-0-9a-zA-Z._+]+\\s*(?:alpha|beta|test|debug)?\\s*(?:[0-9]*))$"
+ ;
+
+ const char* regex2 =
+ "(?:\\bversion\\b(?:[ :]?)|\\brevision\\b(?:[:.\\-]?))[ ]*"
+ "([0-9][-0-9a-zA-Z._]+\\+?)"
+ ;
+
+ const char* regex3 =
+ "(?:\\bver(?:[:.]?)|\\brev(?:[:.]?))\\s*"
+ "([0-9][-0-9a-zA-Z._]*\\+?)"
+ ;
+
+ // Matches "Updated: " for the Bashed patch
+ const char* regex4 =
+ "(?:Updated:)\\s*"
+ "([-0-9aAmMpP/ :]+)$"
+ ;
+
+ // Matches isolated versions as last resort
+ const char* regex5 =
+ "(?:(?:\\bv|\\br)(?:\\s?)(?:[-.:])?(?:\\s*))"
+ "((?:(?:\\balpha\\b)?|(?:\\bbeta\\b)?)\\s*[0-9][-0-9a-zA-Z._]*\\+?)"
+ ;
+
+ // Matches isolated versions as last resort
+ const char* regex6 =
+ "((?:(?:\\balpha\\b)?|(?:\\bbeta\\b)?)\\s*\\b[0-9][-0-9a-zA-Z._]*\\+?)$"
+ ;
+
+ const char* regex7 =
+ "(^\\bmark\\b\\s*\\b[IVX0-9][-0-9a-zA-Z._+]*\\s*(?:alpha|beta|test|debug)?\\s*(?:[0-9]*)?)$"
+ ;
+
+ /// Array used to try each of the expressions defined above using
+ /// an iteration for each of them.
+ regex* version_checks[] = {
+ new regex(regex1, regex::icase),
+ new regex(regex2, regex::icase),
+ new regex(regex3, regex::icase),
+ new regex(regex4, regex::icase),
+ new regex(regex5, regex::icase), //This incorrectly identifies "OBSE v19" where 19 is any integer.
+ new regex(regex6, regex::icase), //This is responsible for metallicow's false positive.
+ new regex(regex7, regex::icase),
+ 0
+ };
+
+}
\ No newline at end of file
diff --git a/src/plugin/VersionRegex.h b/src/plugin/VersionRegex.h
new file mode 100644
index 00000000..c4a2a661
--- /dev/null
+++ b/src/plugin/VersionRegex.h
@@ -0,0 +1,45 @@
+/* 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
+ .
+
+ $Revision: 1681 $, $Date: 2010-10-17 21:01:25 +0100 (Sun, 17 Oct 2010) $
+*/
+
+#ifndef __SUPPORT_VERSIONREGEX__HPP__
+#define __SUPPORT_VERSIONREGEX__HPP__
+
+
+#include
+
+
+namespace boss {
+
+ using namespace boost;
+
+ /// Array used to try each of the expressions defined above using
+ /// an iteration for each of them.
+ extern regex* version_checks[];
+
+}
+
+#endif