diff --git a/MissionEditor/INIMeta.cpp b/MissionEditor/INIMeta.cpp new file mode 100644 index 0000000..cdfdf0e --- /dev/null +++ b/MissionEditor/INIMeta.cpp @@ -0,0 +1,39 @@ +#include "StdAfx.h" +#include "INIMeta.h" +#include + +void IniFileGroup::Append(const CIniFile& INI) +{ + m_group.push_back(&INI); +} + + +const CIniFile* IniFileGroup::Nth(int idx) const +{ + return m_group.at(idx); +} + +const CString& IniFileGroup::GetString(const CString & section, const CString & key) const +{ + for (auto it = m_group.rbegin(); it != m_group.rend(); ++it) { + auto const& got = (*it)->GetString(section, key); + if (!got.IsEmpty()) { + return got; + } + } + return CIniFileSection::EmptyValue; +} + +CString IniFileGroup::GetStringOr(const CString & section, const CString & key, const CString& def) const +{ + auto const& got = this->GetString(section, key); + if (!got.IsEmpty()) { + return got; + } + return def; +} + +IniSectionGroup IniFileGroup::GetSection(const CString& section) const +{ + return IniSectionGroup(*this, section); +} \ No newline at end of file diff --git a/MissionEditor/INIMeta.h b/MissionEditor/INIMeta.h new file mode 100644 index 0000000..deffb31 --- /dev/null +++ b/MissionEditor/INIMeta.h @@ -0,0 +1,142 @@ +#pragma once + +#include +#include "IniFile.h" +#include +#include + +using IndiceStorage = std::vector; +using SequencedSection = std::vector; +class IniSectionGroup; + +/* +* @brief This class uses to simulation a combination of a few ini dictionaries, to + act as they are inside a same ini +*/ +class IniFileGroup +{ + friend class IniMegaFile; +public: + + using StorageType = std::vector; + + IniFileGroup() = default; + + + const CString& GetString(const CString& section, const CString& key) const; + CString GetStringOr(const CString& section, const CString& key, const CString& def) const; + IniSectionGroup GetSection(const CString& section) const; + + auto Size() const { return m_group.size(); } + + void Append(const CIniFile& INI); + const CIniFile* Nth(int idx) const; + + auto begin() const { return m_group.begin(); } + auto end() const { return m_group.end(); } + bool empty() const { return m_group.empty(); } + +private: + StorageType m_group; +}; + +class IniSectionGroup +{ + using KvIter = CIniFileSection::Container::const_iterator; + using GroupIter = IniFileGroup::StorageType::const_iterator; +public: + class Iterator; + friend class Iterator; + class Iterator { + public: + Iterator(const CString& section, + GroupIter groupIter, + GroupIter groupIterEnd, + KvIter kvIter, + KvIter kvIterEnd + ) : + m_section(section), + m_groupIter(groupIter), + m_groupIterEnd(groupIterEnd), + m_kvIter(kvIter), + m_kvIterEnd(kvIterEnd) + { + } + Iterator& operator++() { + // section content still working + if (m_kvIter != std::prev(m_kvIterEnd)) { + m_kvIter++; + return *this; + } + m_groupIter++; + if (m_groupIter != m_groupIterEnd) { + auto [beg, end] = IniSectionGroup::acquireNextKvGroup(m_section, m_groupIter, m_groupIterEnd); + m_kvIter = beg; + m_kvIterEnd = end; + } else { + m_kvIter = m_kvIterEnd;// to make (==) works + } + return *this; + } + + auto const& operator*() const { + return *m_kvIter; + } + bool operator==(const Iterator& rhs) const { + return m_groupIter == rhs.m_groupIter; + } + + private: + GroupIter m_groupIter; + GroupIter m_groupIterEnd; + KvIter m_kvIter; + KvIter m_kvIterEnd; + const CString& m_section; + }; + + IniSectionGroup(const IniFileGroup& source, const CString& secName) : + m_source(source), + m_section(secName) + { + } + + Iterator begin() const { + auto groupBeg = m_source.begin(); + auto [secItBeg, secItEnd] = acquireNextKvGroup(groupBeg); + return Iterator(m_section, groupBeg, m_source.end(), secItBeg, secItEnd); + } + + Iterator end() const { + auto secItEnd = KvIter{}; + if (!m_source.empty()) { + auto const& sec = (*std::prev(m_source.end()))->GetSection(m_section); + secItEnd = sec.end(); + } + return Iterator(m_section, m_source.end(), m_source.end(), secItEnd, secItEnd); + } + +private: + // attention: beginning iter + static std::pair< KvIter, KvIter> acquireNextKvGroup(const CString& section, GroupIter& beg, const GroupIter end) { + + auto secItBeg = KvIter{}; + auto secItEnd = KvIter{}; + for (; beg != end; ++beg) { + auto const& sec = (*beg)->GetSection(section); + if (sec.Size() == 0) { + continue; + } + secItBeg = sec.begin(); + secItEnd = sec.end(); + break; + } + return { secItBeg, secItEnd }; + } + + std::pair< KvIter, KvIter> acquireNextKvGroup(GroupIter& beg) const { + return acquireNextKvGroup(m_section, beg, m_source.end()); + } + + const IniFileGroup& m_source; + CString m_section; +}; diff --git a/MissionEditor/IniFile.h b/MissionEditor/IniFile.h index 4ad31ba..e00c108 100644 --- a/MissionEditor/IniFile.h +++ b/MissionEditor/IniFile.h @@ -43,8 +43,9 @@ using std::map; class CIniFileSection { - static const CString EmptyValue; public: + using Container = vector>; + static const CString EmptyValue; CIniFileSection(); virtual ~CIniFileSection(); @@ -198,7 +199,7 @@ public: private: map value_pos{}; - vector> value_pairs{};// sequenced + Container value_pairs{};// sequenced }; class CIniFile diff --git a/MissionEditor/MissionEditor.vcxproj b/MissionEditor/MissionEditor.vcxproj index 2f8674e..81bdb17 100644 --- a/MissionEditor/MissionEditor.vcxproj +++ b/MissionEditor/MissionEditor.vcxproj @@ -498,6 +498,7 @@ + @@ -609,6 +610,7 @@ + @@ -660,6 +662,7 @@ + diff --git a/MissionEditor/MissionEditor.vcxproj.filters b/MissionEditor/MissionEditor.vcxproj.filters index d54b83f..23f6af9 100644 --- a/MissionEditor/MissionEditor.vcxproj.filters +++ b/MissionEditor/MissionEditor.vcxproj.filters @@ -282,6 +282,9 @@ Source Files + + Source Files + @@ -568,6 +571,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/MissionEditor/StringHelper.h b/MissionEditor/StringHelper.h new file mode 100644 index 0000000..82f9d88 --- /dev/null +++ b/MissionEditor/StringHelper.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace utilities { + static std::vector split_string(const CString& pSource, TCHAR cSplit = ',') + { + std::vector ret; + CString tmp = pSource; + int pos = 0; + + while ((pos = tmp.Find(cSplit)) != -1) { + ret.push_back(tmp.Left(pos)); + tmp = tmp.Mid(pos + 1); + } + + if (!tmp.IsEmpty()) { + ret.push_back(tmp); + } + + return ret; + } +} + diff --git a/UnitTest/CIni_Meta_Test.cpp b/UnitTest/CIni_Meta_Test.cpp new file mode 100644 index 0000000..41a6142 --- /dev/null +++ b/UnitTest/CIni_Meta_Test.cpp @@ -0,0 +1,70 @@ +#include "stdafx.h" +#include "../MissionEditor/INIMeta.h" + +#if 1 +TEST(IniFileGroup, ValidIniWithValidContentTest) { + CIniFile iniFile1; + CIniFile iniFile2; + + const CString referenceList[] = { + CString("GANCST"), + CString("GAPOWR"), + CString("NACNST"), + CString("NAPOWR"), + }; + + iniFile1.SetString("BuildingTypes", "0", "GANCST"); + iniFile1.SetString("BuildingTypes", "1", "GAPOWR"); + iniFile1.SetString("GACNST", "Strength", "1000"); + iniFile1.SetString("E2", "Strength", "100"); + + iniFile2.SetString("BuildingTypes", "2", "NACNST"); + iniFile2.SetString("BuildingTypes", "3", "NAPOWR"); + iniFile2.SetString("GACNST", "Strength", "2000"); + iniFile2.SetString("NEWIFV", "Cost", "1000"); + + IniFileGroup group; + group.Append(iniFile1); + group.Append(iniFile2); + + auto const bldTypes = group.GetSection("BuildingTypes"); + auto idx = 0; + //for (auto const& [key, val] : bldTypes) { + for (auto it = bldTypes.begin(); it != bldTypes.end(); ++it) { + auto const& [key, val] = *it; + EXPECT_EQ(val, referenceList[idx++]); + } + + EXPECT_EQ(group.GetString("GACNST", "Strength"), "2000"); +} +#endif + +TEST(IniFileGroup, EmptyIniContentTest) { + CIniFile iniFile1; + CIniFile iniFile2; + IniFileGroup group; + + group.Append(iniFile1); + group.Append(iniFile2); + + auto const bldTypes = group.GetSection("BuildingTypes"); + auto contentCount = 0; + for (auto it = bldTypes.begin(); it != bldTypes.end(); ++it) { + auto const& [key, val] = *it; + cout << key << "=" << val << endl; + contentCount++; + } + + EXPECT_EQ(contentCount, 0); + + iniFile1.SetString("BuildingTypes", "0", "GANCST"); + + contentCount = 0; + for (auto it = bldTypes.begin(); it != bldTypes.end(); ++it) { + auto const& [key, val] = *it; + cout << key << "=" << val << endl; + contentCount++; + } + + EXPECT_EQ(contentCount, 1); +} \ No newline at end of file diff --git a/UnitTest/StdAfx.h b/UnitTest/StdAfx.h index 457cae7..fe450d1 100644 --- a/UnitTest/StdAfx.h +++ b/UnitTest/StdAfx.h @@ -27,6 +27,9 @@ #include #include +using std::cout; +using std::endl; + #if !defined(ASSERT) #define ASSERT(x) if (!(x)) throw("assertion failed"); #endif \ No newline at end of file diff --git a/UnitTest/UnitTest.vcxproj b/UnitTest/UnitTest.vcxproj index 76b6c5c..077815a 100644 --- a/UnitTest/UnitTest.vcxproj +++ b/UnitTest/UnitTest.vcxproj @@ -102,9 +102,9 @@ Level3 true - _DEBUG;_CONSOLE;_AFXDLL;%(PreprocessorDefinitions) + _DEBUG;NOMINMAX;_CONSOLE;_AFXDLL;%(PreprocessorDefinitions) true - $(SolutionDir)googletest\x64\include;%(AdditionalIncludeDirectories) + $(SolutionDir)googletest\x64\include;$(SolutionDir)MissionEditorPackLib;%(AdditionalIncludeDirectories) Create MultiThreadedDebugDLL stdcpp20 @@ -133,11 +133,14 @@ + + + diff --git a/UnitTest/UnitTest.vcxproj.filters b/UnitTest/UnitTest.vcxproj.filters index 37e3ea3..9de5faa 100644 --- a/UnitTest/UnitTest.vcxproj.filters +++ b/UnitTest/UnitTest.vcxproj.filters @@ -24,6 +24,12 @@ 源文件 + + 源文件 + + + 源文件 + @@ -32,5 +38,8 @@ 头文件 + + 头文件 + \ No newline at end of file