diff --git a/cpp/include/loot/metadata/plugin_cleaning_data.h b/cpp/include/loot/metadata/plugin_cleaning_data.h index 87f10132..fff76009 100644 --- a/cpp/include/loot/metadata/plugin_cleaning_data.h +++ b/cpp/include/loot/metadata/plugin_cleaning_data.h @@ -80,7 +80,8 @@ public: const std::vector& detail, unsigned int itm, unsigned int ref, - unsigned int nav); + unsigned int nav, + std::string_view condition); /** * Get the CRC that identifies the plugin that the cleaning data is for. @@ -122,6 +123,12 @@ public: */ LOOT_API std::vector GetDetail() const; + /** + * Get the condition string. + * @return The cleaning data's condition string. + */ + LOOT_API std::string GetCondition() const; + private: uint32_t crc_{0}; unsigned int itm_{0}; @@ -129,6 +136,7 @@ private: unsigned int nav_{0}; std::string utility_; std::vector detail_; + std::string condition_; }; /** diff --git a/cpp/src/api/convert.cpp b/cpp/src/api/convert.cpp index 11d7c91a..0ee28be9 100644 --- a/cpp/src/api/convert.cpp +++ b/cpp/src/api/convert.cpp @@ -106,7 +106,8 @@ loot::PluginCleaningData convert(const loot::rust::PluginCleaningData& data) { convert(data.detail()), data.itm_count(), data.deleted_reference_count(), - data.deleted_navmesh_count()); + data.deleted_navmesh_count(), + convert(data.condition())); } loot::Location convert(const loot::rust::Location& location) { @@ -221,7 +222,8 @@ loot::rust::MessageType convert(loot::MessageType messageType) { convert(data.GetDetail()), data.GetITMCount(), data.GetDeletedReferenceCount(), - data.GetDeletedNavmeshCount()); + data.GetDeletedNavmeshCount(), + data.GetCondition()); } catch (const ::rust::Error& e) { std::rethrow_exception(mapError(e)); } diff --git a/cpp/src/api/metadata/plugin_cleaning_data.cpp b/cpp/src/api/metadata/plugin_cleaning_data.cpp index 3664d327..fd851b1e 100644 --- a/cpp/src/api/metadata/plugin_cleaning_data.cpp +++ b/cpp/src/api/metadata/plugin_cleaning_data.cpp @@ -34,13 +34,15 @@ PluginCleaningData::PluginCleaningData( const std::vector& detail, unsigned int itm, unsigned int ref, - unsigned int nav) : + unsigned int nav, + std::string_view condition) : crc_(crc), itm_(itm), ref_(ref), nav_(nav), utility_(utility), - detail_(detail) {} + detail_(detail), + condition_(condition) {} uint32_t PluginCleaningData::GetCRC() const { return crc_; } @@ -58,12 +60,15 @@ std::vector PluginCleaningData::GetDetail() const { return detail_; } +std::string PluginCleaningData::GetCondition() const { return condition_; } + bool operator==(const PluginCleaningData& lhs, const PluginCleaningData& rhs) { return lhs.GetCRC() == rhs.GetCRC() && lhs.GetITMCount() == rhs.GetITMCount() && lhs.GetDeletedReferenceCount() == rhs.GetDeletedReferenceCount() && lhs.GetDeletedNavmeshCount() == rhs.GetDeletedNavmeshCount() && lhs.GetCleaningUtility() == rhs.GetCleaningUtility() && + lhs.GetCondition() == rhs.GetCondition() && lhs.GetDetail() == rhs.GetDetail(); } @@ -112,6 +117,14 @@ bool operator<(const PluginCleaningData& lhs, const PluginCleaningData& rhs) { return false; } + if (lhs.GetCondition() < rhs.GetCondition()) { + return true; + } + + if (rhs.GetCondition() < lhs.GetCondition()) { + return false; + } + return lhs.GetDetail() < rhs.GetDetail(); } diff --git a/cpp/src/lib.rs b/cpp/src/lib.rs index 9590d83a..e54714f5 100644 --- a/cpp/src/lib.rs +++ b/cpp/src/lib.rs @@ -612,6 +612,7 @@ mod ffi { itm_count: u32, deleted_reference_count: u32, deleted_navmesh_count: u32, + condition: String, ) -> Result>; pub fn crc(&self) -> u32; @@ -626,6 +627,8 @@ mod ffi { pub fn detail(&self) -> &[MessageContent]; + pub fn condition(&self) -> &str; + pub fn boxed_clone(&self) -> Box; } diff --git a/cpp/src/metadata.rs b/cpp/src/metadata.rs index ffad29e7..994e92b6 100644 --- a/cpp/src/metadata.rs +++ b/cpp/src/metadata.rs @@ -585,6 +585,7 @@ pub fn new_plugin_cleaning_data( itm_count: u32, deleted_reference_count: u32, deleted_navmesh_count: u32, + condition: String, ) -> Result, VerboseError> { let mut data = libloot::metadata::PluginCleaningData::new(crc, cleaning_utility) .with_itm_count(itm_count) @@ -595,6 +596,10 @@ pub fn new_plugin_cleaning_data( data = data.with_detail(to_vec_of_unwrapped(detail))?; } + if !condition.is_empty() { + data = data.with_condition(condition); + } + Ok(Box::new(PluginCleaningData(data))) } @@ -603,6 +608,10 @@ impl PluginCleaningData { MessageContent::wrap_slice(self.0.detail()) } + pub fn condition(&self) -> &str { + self.0.condition().unwrap_or("") + } + pub fn boxed_clone(&self) -> Box { Box::new(Self(self.0.clone())) } diff --git a/cpp/src/tests/api/interface/metadata/plugin_cleaning_data_test.h b/cpp/src/tests/api/interface/metadata/plugin_cleaning_data_test.h index 0d8a6d9c..5ff31531 100644 --- a/cpp/src/tests/api/interface/metadata/plugin_cleaning_data_test.h +++ b/cpp/src/tests/api/interface/metadata/plugin_cleaning_data_test.h @@ -49,264 +49,426 @@ TEST_F(PluginCleaningDataTest, EXPECT_EQ(0u, info.GetDeletedNavmeshCount()); EXPECT_TRUE(info.GetCleaningUtility().empty()); EXPECT_TRUE(info.GetDetail().empty()); + EXPECT_TRUE(info.GetCondition().empty()); } TEST_F(PluginCleaningDataTest, contentConstructorShouldStoreAllGivenData) { - PluginCleaningData info(0x12345678, "cleaner", info_, 2, 10, 30); + PluginCleaningData info(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_EQ(0x12345678u, info.GetCRC()); EXPECT_EQ(2u, info.GetITMCount()); EXPECT_EQ(10u, info.GetDeletedReferenceCount()); EXPECT_EQ(30u, info.GetDeletedNavmeshCount()); EXPECT_EQ("cleaner", info.GetCleaningUtility()); EXPECT_EQ(info_, info.GetDetail()); + EXPECT_EQ("condition", info.GetCondition()); } TEST_F(PluginCleaningDataTest, equalityShouldCheckEqualityOfAllFields) { - PluginCleaningData info1(0x12345678, "cleaner", info_, 2, 10, 30); - PluginCleaningData info2(0x12345678, "cleaner", info_, 2, 10, 30); + PluginCleaningData info1( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + PluginCleaningData info2( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_TRUE(info1 == info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_FALSE(info1 == info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30, "condition"); EXPECT_FALSE(info1 == info2); - info1 = PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30, "condition"); EXPECT_FALSE(info1 == info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData( - 0x12345678, "cleaner", std::vector(), 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = PluginCleaningData(0x12345678, + "cleaner", + std::vector(), + 2, + 10, + 30, + "condition"); EXPECT_FALSE(info1 == info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30, "condition"); EXPECT_FALSE(info1 == info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30, "condition"); EXPECT_FALSE(info1 == info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60, "condition"); + EXPECT_FALSE(info1 == info2); + + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "Condition"); EXPECT_FALSE(info1 == info2); } TEST_F(PluginCleaningDataTest, inequalityShouldBeTheInverseOfEquality) { - PluginCleaningData info1(0x12345678, "cleaner", info_, 2, 10, 30); - PluginCleaningData info2(0x12345678, "cleaner", info_, 2, 10, 30); + PluginCleaningData info1( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + PluginCleaningData info2( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_FALSE(info1 != info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_TRUE(info1 != info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30, "condition"); EXPECT_TRUE(info1 != info2); - info1 = PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30, "condition"); EXPECT_TRUE(info1 != info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData( - 0x12345678, "cleaner", std::vector(), 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = PluginCleaningData(0x12345678, + "cleaner", + std::vector(), + 2, + 10, + 30, + "condition"); EXPECT_TRUE(info1 != info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30, "condition"); EXPECT_TRUE(info1 != info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30, "condition"); EXPECT_TRUE(info1 != info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60, "condition"); + EXPECT_TRUE(info1 != info2); + + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "Condition"); EXPECT_TRUE(info1 != info2); } TEST_F(PluginCleaningDataTest, lessThanOperatorShouldCompareAllFields) { - PluginCleaningData info1(0x12345678, "cleaner", info_, 2, 10, 30); - PluginCleaningData info2(0x12345678, "cleaner", info_, 2, 10, 30); + PluginCleaningData info1( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + PluginCleaningData info2( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_FALSE(info1 < info2); EXPECT_FALSE(info2 < info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_TRUE(info1 < info2); EXPECT_FALSE(info2 < info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30, "condition"); EXPECT_TRUE(info2 < info1); EXPECT_FALSE(info1 < info2); - info1 = PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30, "condition"); EXPECT_TRUE(info1 < info2); EXPECT_FALSE(info2 < info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData( - 0x12345678, "cleaner", std::vector(), 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = PluginCleaningData(0x12345678, + "cleaner", + std::vector(), + 2, + 10, + 30, + "condition"); EXPECT_TRUE(info2 < info1); EXPECT_FALSE(info1 < info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30, "condition"); EXPECT_TRUE(info1 < info2); EXPECT_FALSE(info2 < info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30, "condition"); EXPECT_TRUE(info1 < info2); EXPECT_FALSE(info2 < info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60, "condition"); EXPECT_TRUE(info1 < info2); EXPECT_FALSE(info2 < info1); + + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "Condition"); + EXPECT_TRUE(info2 < info1); + EXPECT_FALSE(info1 < info2); } TEST_F( PluginCleaningDataTest, greaterThanOperatorShouldReturnTrueIfTheSecondPluginCleaningDataIsLessThanTheFirst) { - PluginCleaningData info1(0x12345678, "cleaner", info_, 2, 10, 30); - PluginCleaningData info2(0x12345678, "cleaner", info_, 2, 10, 30); + PluginCleaningData info1( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + PluginCleaningData info2( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_FALSE(info1 > info2); EXPECT_FALSE(info2 > info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_FALSE(info1 > info2); EXPECT_TRUE(info2 > info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30, "condition"); EXPECT_FALSE(info2 > info1); EXPECT_TRUE(info1 > info2); - info1 = PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30, "condition"); EXPECT_FALSE(info1 > info2); EXPECT_TRUE(info2 > info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData( - 0x12345678, "cleaner", std::vector(), 2, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = PluginCleaningData(0x12345678, + "cleaner", + std::vector(), + 2, + 10, + 30, + "condition"); EXPECT_FALSE(info2 > info1); EXPECT_TRUE(info1 > info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30, "condition"); EXPECT_FALSE(info1 > info2); EXPECT_TRUE(info2 > info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30, "condition"); EXPECT_FALSE(info1 > info2); EXPECT_TRUE(info2 > info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60, "condition"); EXPECT_FALSE(info1 > info2); EXPECT_TRUE(info2 > info1); + + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "Condition"); + EXPECT_FALSE(info2 > info1); + EXPECT_TRUE(info1 > info2); } TEST_F( PluginCleaningDataTest, lessThanOrEqualOperatorShouldReturnTrueIfTheFirstPluginCleaningDataIsNotGreaterThanTheSecond) { - PluginCleaningData info1(0x12345678, "cleaner", info_, 2, 10, 30); - PluginCleaningData info2(0x12345678, "cleaner", info_, 2, 10, 30); + PluginCleaningData info1( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + PluginCleaningData info2( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_TRUE(info1 <= info2); EXPECT_TRUE(info2 <= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30); - EXPECT_TRUE(info1 < info2); - EXPECT_FALSE(info2 < info1); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30, "condition"); + EXPECT_TRUE(info1 <= info2); + EXPECT_FALSE(info2 <= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30); - EXPECT_TRUE(info2 < info1); - EXPECT_FALSE(info1 < info2); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30, "condition"); + EXPECT_TRUE(info2 <= info1); + EXPECT_FALSE(info1 <= info2); - info1 = PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30); - EXPECT_TRUE(info1 < info2); - EXPECT_FALSE(info2 < info1); + info1 = + PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30, "condition"); + EXPECT_TRUE(info1 <= info2); + EXPECT_FALSE(info2 <= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData( - 0x12345678, "cleaner", std::vector(), 2, 10, 30); - EXPECT_TRUE(info2 < info1); - EXPECT_FALSE(info1 < info2); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = PluginCleaningData(0x12345678, + "cleaner", + std::vector(), + 2, + 10, + 30, + "condition"); + EXPECT_TRUE(info2 <= info1); + EXPECT_FALSE(info1 <= info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30); - EXPECT_TRUE(info1 < info2); - EXPECT_FALSE(info2 < info1); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30, "condition"); + EXPECT_TRUE(info1 <= info2); + EXPECT_FALSE(info2 <= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30); - EXPECT_TRUE(info1 < info2); - EXPECT_FALSE(info2 < info1); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30, "condition"); + EXPECT_TRUE(info1 <= info2); + EXPECT_FALSE(info2 <= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60); - EXPECT_TRUE(info1 < info2); - EXPECT_FALSE(info2 < info1); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60, "condition"); + EXPECT_TRUE(info1 <= info2); + EXPECT_FALSE(info2 <= info1); + + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "Condition"); + EXPECT_TRUE(info2 <= info1); + EXPECT_FALSE(info1 <= info2); } TEST_F( PluginCleaningDataTest, greaterThanOrEqualToOperatorShouldReturnTrueIfTheFirstPluginCleaningDataIsNotLessThanTheSecond) { - PluginCleaningData info1(0x12345678, "cleaner", info_, 2, 10, 30); - PluginCleaningData info2(0x12345678, "cleaner", info_, 2, 10, 30); + PluginCleaningData info1( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + PluginCleaningData info2( + 0x12345678, "cleaner", info_, 2, 10, 30, "condition"); EXPECT_TRUE(info1 >= info2); EXPECT_TRUE(info2 >= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30); - EXPECT_FALSE(info1 > info2); - EXPECT_TRUE(info2 > info1); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30, "condition"); + EXPECT_FALSE(info1 >= info2); + EXPECT_TRUE(info2 >= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30); - EXPECT_FALSE(info2 > info1); - EXPECT_TRUE(info1 > info2); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "Cleaner", info_, 2, 10, 30, "condition"); + EXPECT_FALSE(info2 >= info1); + EXPECT_TRUE(info1 >= info2); - info1 = PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30); - EXPECT_FALSE(info1 > info2); - EXPECT_TRUE(info2 > info1); + info1 = + PluginCleaningData(0x12345678, "cleaner1", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner2", info_, 2, 10, 30, "condition"); + EXPECT_FALSE(info1 >= info2); + EXPECT_TRUE(info2 >= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData( - 0x12345678, "cleaner", std::vector(), 2, 10, 30); - EXPECT_FALSE(info2 > info1); - EXPECT_TRUE(info1 > info2); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = PluginCleaningData(0x12345678, + "cleaner", + std::vector(), + 2, + 10, + 30, + "condition"); + EXPECT_FALSE(info2 >= info1); + EXPECT_TRUE(info1 >= info2); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30); - EXPECT_FALSE(info1 > info2); - EXPECT_TRUE(info2 > info1); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 4, 10, 30, "condition"); + EXPECT_FALSE(info1 >= info2); + EXPECT_TRUE(info2 >= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30); - EXPECT_FALSE(info1 > info2); - EXPECT_TRUE(info2 > info1); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 20, 30, "condition"); + EXPECT_FALSE(info1 >= info2); + EXPECT_TRUE(info2 >= info1); - info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30); - info2 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60); - EXPECT_FALSE(info1 > info2); - EXPECT_TRUE(info2 > info1); + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 60, "condition"); + EXPECT_FALSE(info1 >= info2); + EXPECT_TRUE(info2 >= info1); + + info1 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "condition"); + info2 = + PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30, "Condition"); + EXPECT_FALSE(info2 >= info1); + EXPECT_TRUE(info1 >= info2); } } diff --git a/cpp/src/tests/api/interface/metadata/plugin_metadata_test.h b/cpp/src/tests/api/interface/metadata/plugin_metadata_test.h index 2f9af8ba..c3e1cbe2 100644 --- a/cpp/src/tests/api/interface/metadata/plugin_metadata_test.h +++ b/cpp/src/tests/api/interface/metadata/plugin_metadata_test.h @@ -205,8 +205,8 @@ TEST_F(PluginMetadataTest, mergeMetadataShouldMergeTags) { TEST_F(PluginMetadataTest, mergeMetadataShouldMergeDirtyInfoData) { PluginMetadata plugin1; PluginMetadata plugin2; - PluginCleaningData info1(0x5, "utility", info_, 1, 2, 3); - PluginCleaningData info2(0xA, "utility", info_, 1, 2, 3); + PluginCleaningData info1(0x5, "utility", info_, 1, 2, 3, "condition"); + PluginCleaningData info2(0xA, "utility", info_, 1, 2, 3, "condition"); plugin1.SetDirtyInfo({info1}); plugin2.SetDirtyInfo({info1, info2}); @@ -314,7 +314,8 @@ TEST_F(PluginMetadataTest, hasNameOnlyShouldBeFalseIfTagsExist) { TEST_F(PluginMetadataTest, hasNameOnlyShouldBeFalseIfDirtyInfoExists) { PluginMetadata plugin(blankEsp); - plugin.SetDirtyInfo({PluginCleaningData(5, "utility", info_, 0, 1, 2)}); + plugin.SetDirtyInfo( + {PluginCleaningData(5, "utility", info_, 0, 1, 2, "condition")}); EXPECT_FALSE(plugin.HasNameOnly()); } diff --git a/nodejs/src/metadata.rs b/nodejs/src/metadata.rs index 8413b5b5..befcaaa7 100644 --- a/nodejs/src/metadata.rs +++ b/nodejs/src/metadata.rs @@ -317,6 +317,7 @@ impl PluginCleaningData { deleted_reference_count: Option, deleted_navmesh_count: Option, detail: Option>, + condition: Option, ) -> Result { let mut data = libloot::metadata::PluginCleaningData::new(crc, cleaning_utility); @@ -337,6 +338,10 @@ impl PluginCleaningData { data = data.with_detail(detail)?; } + if let Some(condition) = condition { + data = data.with_condition(condition); + } + Ok(Self(data)) } @@ -369,6 +374,11 @@ impl PluginCleaningData { pub fn detail(&self) -> Vec { self.0.detail().iter().cloned().map(Into::into).collect() } + + #[napi(getter)] + pub fn condition(&self) -> Option<&str> { + self.0.condition() + } } impl From for PluginCleaningData { diff --git a/python/src/metadata.rs b/python/src/metadata.rs index 42b310b0..b90e8098 100644 --- a/python/src/metadata.rs +++ b/python/src/metadata.rs @@ -391,7 +391,7 @@ pub struct PluginCleaningData(libloot::metadata::PluginCleaningData); #[pymethods] impl PluginCleaningData { #[new] - #[pyo3(signature = (crc, cleaning_utility, itm_count = None, deleted_reference_count = None, deleted_navmesh_count = None, detail = None))] + #[pyo3(signature = (crc, cleaning_utility, itm_count = None, deleted_reference_count = None, deleted_navmesh_count = None, detail = None, condition = None))] fn new( crc: u32, cleaning_utility: String, @@ -399,6 +399,7 @@ impl PluginCleaningData { deleted_reference_count: Option, deleted_navmesh_count: Option, detail: Option>, + condition: Option, ) -> Result { let mut data = libloot::metadata::PluginCleaningData::new(crc, cleaning_utility); @@ -419,6 +420,10 @@ impl PluginCleaningData { data = data.with_detail(detail)?; } + if let Some(condition) = condition { + data = data.with_condition(condition); + } + Ok(Self(data)) } @@ -452,18 +457,24 @@ impl PluginCleaningData { self.0.detail().iter().cloned().map(Into::into).collect() } + #[getter] + fn condition(&self) -> Option<&str> { + self.0.condition() + } + fn __repr__(slf: &Bound<'_, Self>) -> PyResult { let class_name = slf.get_type().qualname()?; let inner = &slf.borrow().0; Ok(format!( - "{}({}, {}, {}, {}, {}, {})", + "{}({}, {}, {}, {}, {}, {}, {})", class_name, inner.crc(), inner.cleaning_utility(), inner.itm_count(), inner.deleted_reference_count(), inner.deleted_navmesh_count(), - repr_message_contents(inner.detail()) + repr_message_contents(inner.detail()), + inner.condition().unwrap_or(NONE_REPR) )) } } diff --git a/src/database/conditions.rs b/src/database/conditions.rs index 7598f62b..bad6d2f9 100644 --- a/src/database/conditions.rs +++ b/src/database/conditions.rs @@ -107,7 +107,11 @@ fn filter_cleaning_data_on_conditions( cleaning_info .iter() .filter_map(|i| { - let condition = format!("checksum(\"{}\", {:08X})", plugin_name, i.crc()); + let condition = if let Some(c) = i.condition() { + format!("checksum(\"{}\", {:08X}) and ({})", plugin_name, i.crc(), c) + } else { + format!("checksum(\"{}\", {:08X})", plugin_name, i.crc()) + }; filter_map_on_condition(i, Some(condition.as_str()), state) }) @@ -152,7 +156,9 @@ mod tests { let info1 = PluginCleaningData::new(0x374E_2A6F, "utility1".into()); let info2 = PluginCleaningData::new(0xDEAD_BEEF, "utility2".into()); - plugin.set_dirty_info(vec![info1.clone(), info2.clone()]); + let info3 = PluginCleaningData::new(0x374E_2A6F, "utility3".into()) + .with_condition(condition.clone()); + plugin.set_dirty_info(vec![info1.clone(), info2.clone(), info3.clone()]); plugin.set_clean_info(vec![info1.clone(), info2.clone()]); let state = loot_condition_interpreter::State::new( diff --git a/src/metadata/metadata_document.rs b/src/metadata/metadata_document.rs index 70aa1097..9e01e5f5 100644 --- a/src/metadata/metadata_document.rs +++ b/src/metadata/metadata_document.rs @@ -632,12 +632,20 @@ fn get_common_metadata_values<'a>( if !info.detail().is_empty() { message_contents_counts.increment_for(info.detail()); } + + if let Some(condition) = info.condition() { + condition_counts.increment_for(condition); + } } for info in plugin.clean_info() { if !info.detail().is_empty() { message_contents_counts.increment_for(info.detail()); } + + if let Some(condition) = info.condition() { + condition_counts.increment_for(condition); + } } } @@ -1468,6 +1476,7 @@ plugins: let condition1 = "file(\"test.txt\")"; let condition2 = "file(\"other.txt\")"; let condition3 = "file(\"third.txt\")"; + let condition4 = "file(\"fourth.txt\")"; let contents1 = vec![MessageContent::new("message text 1".to_owned())]; let contents2 = vec![ MessageContent::new("message text 1".to_owned()).with_language("en".to_owned()), @@ -1502,10 +1511,12 @@ plugins: let info1 = PluginCleaningData::new(0xDEAD_BEEF, "utility".to_owned()) .with_detail(contents2.clone()) - .unwrap(); + .unwrap() + .with_condition(condition4.to_owned()); let info2 = PluginCleaningData::new(0xDEAD_BEEF, "utility".to_owned()) .with_detail(contents3.clone()) - .unwrap(); + .unwrap() + .with_condition(condition4.to_owned()); plugin.set_load_after_files(vec![file1.clone()]); plugin.set_requirements(vec![file1.clone(), file2.clone()]); @@ -1583,10 +1594,12 @@ plugins: - crc: 0xDEADBEEF util: 'utility' detail: *contents2 + condition: &condition4 'file(\"fourth.txt\")' clean: - crc: 0xDEADBEEF util: 'utility' detail: &contents3 'message text 3' + condition: *condition4 - name: 'test2.esp' after: @@ -1641,6 +1654,8 @@ plugins: - &condition3 'file(\"third.txt\")' + - &condition4 'file(\"fourth.txt\")' + - &message1 type: say content: *contents2 @@ -1682,10 +1697,12 @@ plugins: - crc: 0xDEADBEEF util: 'utility' detail: *contents2 + condition: *condition4 clean: - crc: 0xDEADBEEF util: 'utility' detail: *contents3 + condition: *condition4 - name: 'test2.esp' after: @@ -1738,6 +1755,8 @@ plugins: - &condition3 'file(\"third.txt\")' + - &condition4 'file(\"fourth.txt\")' + - &message1 type: say content: *contents2 @@ -1777,10 +1796,12 @@ plugins: - crc: 0xDEADBEEF util: 'utility' detail: *contents2 + condition: *condition4 clean: - crc: 0xDEADBEEF util: 'utility' detail: *contents3 + condition: *condition4 - name: 'test2.esp' after: diff --git a/src/metadata/plugin_cleaning_data.rs b/src/metadata/plugin_cleaning_data.rs index 321c264a..c6a18180 100644 --- a/src/metadata/plugin_cleaning_data.rs +++ b/src/metadata/plugin_cleaning_data.rs @@ -1,5 +1,7 @@ use saphyr::MarkedYaml; +use crate::metadata::yaml::parse_condition; + use super::{ error::{MultilingualMessageContentsError, ParseMetadataError}, message::{ @@ -22,6 +24,7 @@ pub struct PluginCleaningData { deleted_navmesh_count: u32, cleaning_utility: Box, detail: Box<[MessageContent]>, + condition: Option>, } impl PluginCleaningData { @@ -69,6 +72,13 @@ impl PluginCleaningData { Ok(self) } + /// Set the condition string. + #[must_use] + pub fn with_condition(mut self, condition: String) -> Self { + self.condition = Some(condition.into_boxed_str()); + self + } + /// Get the CRC that identifies the plugin that the cleaning data is for. pub fn crc(&self) -> u32 { self.crc @@ -104,6 +114,11 @@ impl PluginCleaningData { pub fn detail(&self) -> &[MessageContent] { &self.detail } + + /// Get the condition string. + pub fn condition(&self) -> Option<&str> { + self.condition.as_deref() + } } impl TryFromYaml for PluginCleaningData { @@ -136,6 +151,8 @@ impl TryFromYaml for PluginCleaningData { None => Box::default(), }; + let condition = parse_condition(mapping, "condition", YamlObjectType::File)?; + Ok(PluginCleaningData { crc, itm_count: itm, @@ -143,6 +160,7 @@ impl TryFromYaml for PluginCleaningData { deleted_navmesh_count: nav, cleaning_utility: util.into(), detail, + condition, }) } } @@ -177,6 +195,11 @@ impl EmitYaml for PluginCleaningData { emit_message_contents(&self.detail, emitter); } + if let Some(condition) = &self.condition { + emitter.write_map_key("condition"); + emitter.write_condition(condition); + } + emitter.end_map(); } } @@ -250,8 +273,9 @@ mod tests { #[test] fn should_set_all_given_fields() { - let yaml = - parse("{crc: 0x12345678, util: cleaner, detail: info, itm: 2, udr: 10, nav: 30}"); + let yaml = parse( + "{crc: 0x12345678, util: cleaner, detail: info, itm: 2, udr: 10, nav: 30, condition: file(\"example.esp\")}", + ); let data = PluginCleaningData::try_from_yaml(&yaml).unwrap(); @@ -261,6 +285,7 @@ mod tests { assert_eq!(2, data.itm_count()); assert_eq!(10, data.deleted_reference_count()); assert_eq!(30, data.deleted_navmesh_count()); + assert_eq!("file(\"example.esp\")", data.condition().unwrap()); } #[test] @@ -275,6 +300,7 @@ mod tests { assert_eq!(0, data.itm_count()); assert_eq!(0, data.deleted_reference_count()); assert_eq!(0, data.deleted_navmesh_count()); + assert!(data.condition().is_none()); } #[test] @@ -400,7 +426,8 @@ detail: MessageContent::new("english".into()).with_language("en".into()), MessageContent::new("french".into()).with_language("fr".into()), ]) - .unwrap(); + .unwrap() + .with_condition("file(\"example.esp\")".into()); let yaml = emit(&data); assert_eq!( @@ -414,7 +441,8 @@ detail: - lang: {} text: '{}' - lang: {} - text: '{}'", + text: '{}' +condition: '{}'", data.cleaning_utility, data.itm_count, data.deleted_reference_count, @@ -422,7 +450,8 @@ detail: data.detail[0].language(), data.detail[0].text(), data.detail[1].language(), - data.detail[1].text() + data.detail[1].text(), + data.condition.unwrap() ), yaml ); @@ -445,5 +474,25 @@ detail: assert_eq!("crc: 0xDEADBEEF\nutil: 'TES5Edit'\ndetail: *content1", yaml); } + + #[test] + fn should_emit_an_alias_if_the_condition_has_an_anchor() { + let data = PluginCleaningData::new(0xDEAD_BEEF, "TES5Edit".into()) + .with_condition("file(\"example.esp\")".into()); + + let mut anchors = YamlAnchors::new(); + anchors.set_condition_anchors(HashMap::from([( + data.condition().unwrap(), + "condition1".to_owned(), + )])); + anchors.record_written_anchor("condition1".to_owned()); + + let yaml = emit_with_anchors(&data, anchors); + + assert_eq!( + "crc: 0xDEADBEEF\nutil: 'TES5Edit'\ncondition: *condition1", + yaml + ); + } } }