From d63b709ff2c099b4454cfbc2893b8245df790d30 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Thu, 6 Sep 2018 21:39:11 +0100 Subject: [PATCH] Cache CRCs alongside conditions --- src/api/game/game_cache.cpp | 18 +++++ src/api/game/game_cache.h | 4 + src/api/metadata/condition_evaluator.cpp | 79 +++++++++---------- src/api/metadata/condition_evaluator.h | 2 + .../api/internals/game/game_cache_test.h | 13 ++- 5 files changed, 75 insertions(+), 41 deletions(-) diff --git a/src/api/game/game_cache.cpp b/src/api/game/game_cache.cpp index 7771f624..e4451f29 100644 --- a/src/api/game/game_cache.cpp +++ b/src/api/game/game_cache.cpp @@ -67,6 +67,23 @@ std::pair GameCache::GetCachedCondition( return pair(false, false); } +uint32_t GameCache::GetCachedCrc(const std::string& file) const { + lock_guard guard(mutex_); + + auto it = crcs_.find(to_lower(file)); + + if (it != crcs_.end()) { + return it->second; + } + + return 0; +} + +void GameCache::CacheCrc(const std::string& file, uint32_t crc) { + lock_guard guard(mutex_); + crcs_.insert(pair(to_lower(file), crc)); +} + std::set> GameCache::GetPlugins() const { std::set> output; std::transform( @@ -116,6 +133,7 @@ void GameCache::ClearCachedConditions() { lock_guard guard(mutex_); conditions_.clear(); + crcs_.clear(); } void GameCache::ClearCachedPlugins() { diff --git a/src/api/game/game_cache.h b/src/api/game/game_cache.h index 5a718572..65311ba4 100644 --- a/src/api/game/game_cache.h +++ b/src/api/game/game_cache.h @@ -43,6 +43,9 @@ public: std::pair GetCachedCondition(const std::string& condition) const; void CacheCondition(const std::string& condition, bool result); + uint32_t GetCachedCrc(const std::string& file) const; + void CacheCrc(const std::string& file, uint32_t crc); + std::set> GetPlugins() const; std::shared_ptr GetPlugin(const std::string& pluginName) const; void AddPlugin(const Plugin&& plugin); @@ -56,6 +59,7 @@ public: private: std::unordered_map conditions_; + std::unordered_map crcs_; std::unordered_map> plugins_; std::set archivePaths_; diff --git a/src/api/metadata/condition_evaluator.cpp b/src/api/metadata/condition_evaluator.cpp index c3c76502..f11a4e19 100644 --- a/src/api/metadata/condition_evaluator.cpp +++ b/src/api/metadata/condition_evaluator.cpp @@ -75,25 +75,7 @@ bool ConditionEvaluator::evaluate(const PluginCleaningData& cleaningData, if (shouldParseOnly() || pluginName.empty()) return false; - // First need to get plugin's CRC. - uint32_t crc = 0; - - // Get the CRC from the game plugin cache if possible. - try { - crc = gameCache_->GetPlugin(pluginName)->GetCRC(); - } catch (...) { - } - - // Otherwise calculate it from the file. - if (crc == 0) { - if (boost::filesystem::exists(dataPath_ / pluginName)) { - crc = GetCrc32(dataPath_ / pluginName); - } else if (boost::filesystem::exists(dataPath_ / (pluginName + ".ghost"))) { - crc = GetCrc32(dataPath_ / (pluginName + ".ghost")); - } - } - - return cleaningData.GetCRC() == crc; + return cleaningData.GetCRC() == getCrc(pluginName); } PluginMetadata ConditionEvaluator::evaluateAll( @@ -256,27 +238,7 @@ bool ConditionEvaluator::checksumMatches(const std::string& filePath, if (shouldParseOnly()) return false; - uint32_t realChecksum = 0; - if (filePath == "LOOT") - realChecksum = GetCrc32(boost::filesystem::absolute("LOOT.exe")); - else { - // CRC could be for a plugin or a file. - // Get the CRC from the game plugin cache if possible. - try { - realChecksum = gameCache_->GetPlugin(filePath)->GetCRC(); - } catch (...) { - } - - if (realChecksum == 0) { - if (boost::filesystem::exists(dataPath_ / filePath)) - realChecksum = GetCrc32(dataPath_ / filePath); - else if (hasPluginFileExtension(filePath, gameType_) && - boost::filesystem::exists(dataPath_ / (filePath + ".ghost"))) - realChecksum = GetCrc32(dataPath_ / (filePath + ".ghost")); - } - } - - return checksum == realChecksum; + return checksum == getCrc(filePath); } bool ConditionEvaluator::compareVersions(const std::string& filePath, @@ -468,4 +430,41 @@ Version ConditionEvaluator::getVersion(const std::string& filePath) const { bool ConditionEvaluator::shouldParseOnly() const { return gameCache_ == nullptr || loadOrderHandler_ == nullptr; } + +uint32_t ConditionEvaluator::getCrc(const std::string & file) const { + uint32_t crc = gameCache_->GetCachedCrc(file); + + if (crc != 0) { + return crc; + } + + if (file == "LOOT") { + crc = GetCrc32(boost::filesystem::absolute("LOOT.exe")); + gameCache_->CacheCrc(file, crc); + return crc; + } + + // Get the CRC from the game plugin cache if possible. + try { + crc = gameCache_->GetPlugin(file)->GetCRC(); + } catch (...) { + } + + // Otherwise calculate it from the file. + if (crc == 0) { + if (boost::filesystem::exists(dataPath_ / file)) { + crc = GetCrc32(dataPath_ / file); + } + else if (hasPluginFileExtension(file, gameType_) && + boost::filesystem::exists(dataPath_ / (file + ".ghost"))) { + crc = GetCrc32(dataPath_ / (file + ".ghost")); + } + } + + if (crc != 0) { + gameCache_->CacheCrc(file, crc); + } + + return crc; +} } diff --git a/src/api/metadata/condition_evaluator.h b/src/api/metadata/condition_evaluator.h index 5bfeb0a2..10c182cb 100644 --- a/src/api/metadata/condition_evaluator.h +++ b/src/api/metadata/condition_evaluator.h @@ -92,6 +92,8 @@ private: bool shouldParseOnly() const; + uint32_t getCrc(const std::string& file) const; + const GameType gameType_; const boost::filesystem::path dataPath_; const std::shared_ptr gameCache_; diff --git a/src/tests/api/internals/game/game_cache_test.h b/src/tests/api/internals/game/game_cache_test.h index 4669942e..f2e0a587 100644 --- a/src/tests/api/internals/game/game_cache_test.h +++ b/src/tests/api/internals/game/game_cache_test.h @@ -70,6 +70,15 @@ TEST_P(GameCacheTest, gettingANonCachedConditionShouldReturnAFalseFalsePair) { EXPECT_EQ(std::make_pair(false, false), cache_.GetCachedCondition(condition)); } +TEST_P(GameCacheTest, gettingACachedCrcShouldReturnTheValue) { + cache_.CacheCrc(boost::locale::to_upper(blankEsm), 5); + EXPECT_EQ(5, cache_.GetCachedCrc(blankEsm)); +} + +TEST_P(GameCacheTest, gettingAnUncachedCrcShouldReturnZero) { + EXPECT_EQ(0, cache_.GetCachedCrc(blankEsm)); +} + TEST_P(GameCacheTest, addingAPluginThatDoesNotExistShouldSucceed) { cache_.AddPlugin(Plugin(game_.Type(), game_.DataPath(), @@ -159,13 +168,15 @@ TEST_P(GameCacheTest, EXPECT_NO_THROW(cache_.ClearCachedConditions()); } -TEST_P(GameCacheTest, clearingCachedConditionsShouldClearAnyCachedConditions) { +TEST_P(GameCacheTest, clearingCachedConditionsShouldClearAnyCachedConditionsAndCrcs) { EXPECT_NO_THROW(cache_.CacheCondition(condition, true)); + cache_.CacheCrc(blankEsm, 5); EXPECT_NO_THROW(cache_.ClearCachedConditions()); EXPECT_EQ(std::make_pair(false, false), cache_.GetCachedCondition(conditionLowercase)); + EXPECT_EQ(0, cache_.GetCachedCrc(blankEsm)); } TEST_P(GameCacheTest, clearingCachedPluginsShouldNotThrowIfNoPluginsAreCached) {