diff --git a/MissionEditor/IniFile.h b/MissionEditor/IniFile.h index f2c7057..5b74d5d 100644 --- a/MissionEditor/IniFile.h +++ b/MissionEditor/IniFile.h @@ -137,6 +137,15 @@ public: return this->FindValue(val) >= 0; } + size_t LowerBound(const CString& key) const { + auto const it = value_pos.lower_bound(key); + if (it != value_pos.end()) { + return it->second; + } + return value_pairs.size(); + } + + // ==================== Modify void SetString(const CString& key, const CString& value) { return this->SetString(key, CString(value)); } @@ -145,7 +154,7 @@ public: auto const it = value_pos.find(key); // new, never had one if (it == value_pos.end()) { - this->Insert(key, std::move(value)); + this->Append(key, std::move(value)); return; } value_pairs[it->second].second = std::move(value); @@ -159,15 +168,31 @@ public: this->SetString(key, INIHelper::ToString(val)); } - void Insert(const CString& key, const CString& value) { - this->Insert(key, CString(value)); + void Append(const CString& key, const CString& value) { + this->Append(key, CString(value)); } - void Insert(const CString& key, CString&& value) { + void Append(const CString& key, CString&& value) { value_pairs.push_back({ key, value }); value_pos.insert_or_assign(key, value_pairs.size() - 1); } + void InsertAt(size_t idx, CString&& key, CString&& value) { + if (idx > value_pairs.size()) { + idx = value_pairs.size() - 1; + } + value_pairs.insert(value_pairs.begin() + idx, { key, value }); + value_pos.insert_or_assign(key, idx); + // fix all indexes + for (auto it = value_pos.upper_bound(key); it != value_pos.end(); ++it) { + it->second++; + } + } + void Insert(CString&& key, CString&& value) { + auto const pos = LowerBound(key); + InsertAt(pos, std::move(key), std::move(value)); + } + // ==================== Delete void RemoveAt(size_t idx) { @@ -309,7 +334,7 @@ public: // ============== Writer and Helper converter ============================ CIniFileSection& AddSection(CString&& sectionName) { - auto const ret = this->sections.insert({ std::move(sectionName), {} }); + auto const ret = this->sections.try_emplace(std::move(sectionName), CIniFileSection{}); return ret.first->second; } CIniFileSection& AddSection(const CString& sectionName) { diff --git a/MissionEditor/Loading.cpp b/MissionEditor/Loading.cpp index 984cb98..503a263 100644 --- a/MissionEditor/Loading.cpp +++ b/MissionEditor/Loading.cpp @@ -4216,7 +4216,7 @@ void CLoading::HackRules() auto const pSec = rules.TryGetSection("BuildingTypes"); ASSERT(pSec != nullptr); if (!pSec->HasValue(gateOne)) { - pSec->Insert(gateOne, gateOne); + pSec->Append(gateOne, gateOne); } } diff --git a/UnitTest/CIni_Test.cpp b/UnitTest/CIni_Test.cpp index fc2a8de..1895cac 100644 --- a/UnitTest/CIni_Test.cpp +++ b/UnitTest/CIni_Test.cpp @@ -237,4 +237,38 @@ CliffsWater2=112 EXPECT_EQ(0, file.GetInteger("NewUrbanInfo", "Morphable2")); EXPECT_EQ(false, file["NewUrbanInfo"].Exists("Morphable2")); +} + +TEST(CIniFileClass, IniLowerBoundInsertTest) { + auto const fileName = "test.ini"; + IniTestHelper helper(fileName, R"( +[Waypoints] +0=123456 +1=456123 +2=123654 +5=789654 +6=654789 +10=159357 + +)"); + + CIniFile file; + ASSERT_EQ(file.LoadFile(std::string(fileName)), 0); + + EXPECT_EQ(123654, file.GetInteger("Waypoints", "2")); + + auto const pSec = file.TryGetSection("Waypoints"); + EXPECT_NE(pSec, nullptr); + auto const pos = pSec->LowerBound("4"); + EXPECT_LE(pos, pSec->Size()); + pSec->InsertAt(pos, "4", "432156"); + EXPECT_EQ(432156, file.GetInteger("Waypoints", "4")); + EXPECT_EQ("432156", file["Waypoints"].Nth(3).second); + EXPECT_EQ("789654", file["Waypoints"].Nth(4).second); + pSec->Insert("9", "149367"); + pSec->Insert("11", "987654"); + EXPECT_EQ(149367, file.GetInteger("Waypoints", "9")); + EXPECT_EQ(987654, file.GetInteger("Waypoints", "11")); + EXPECT_EQ("987654", file["Waypoints"].Nth(pSec->Size() - 1).second); + EXPECT_EQ("159357", file["Waypoints"].Nth(pSec->Size() - 2).second); } \ No newline at end of file