Implemented IniMeta helpers to fetch combination of ini files .

This commit is contained in:
Zero Fanker 2024-04-25 22:14:06 -04:00
parent d1a32df54b
commit 90fa8eb408
10 changed files with 308 additions and 4 deletions

39
MissionEditor/INIMeta.cpp Normal file
View file

@ -0,0 +1,39 @@
#include "StdAfx.h"
#include "INIMeta.h"
#include <set>
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);
}

142
MissionEditor/INIMeta.h Normal file
View file

@ -0,0 +1,142 @@
#pragma once
#include <afx.h>
#include "IniFile.h"
#include <vector>
#include <map>
using IndiceStorage = std::vector<CString>;
using SequencedSection = std::vector<CString>;
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<const CIniFile*>;
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;
};

View file

@ -43,8 +43,9 @@ using std::map;
class CIniFileSection
{
static const CString EmptyValue;
public:
using Container = vector<std::pair<CString, CString>>;
static const CString EmptyValue;
CIniFileSection();
virtual ~CIniFileSection();
@ -198,7 +199,7 @@ public:
private:
map<CString, int64_t> value_pos{};
vector<std::pair<CString, CString>> value_pairs{};// sequenced
Container value_pairs{};// sequenced
};
class CIniFile

View file

@ -498,6 +498,7 @@
<ClCompile Include="Infantry.cpp" />
<ClCompile Include="Info.cpp" />
<ClCompile Include="IniFile.cpp" />
<ClCompile Include="INIMeta.cpp" />
<ClCompile Include="InputBox.cpp" />
<ClCompile Include="IsoView.cpp" />
<ClCompile Include="Lighting.cpp" />
@ -609,6 +610,7 @@
<ClInclude Include="ComboUInputDlg.h" />
<ClInclude Include="defines.h" />
<ClInclude Include="IniHelper.h" />
<ClInclude Include="INIMeta.h" />
<ClInclude Include="LineDrawer.h" />
<ClInclude Include="DynamicGraphDlg.h" />
<ClInclude Include="FinalSun.h" />
@ -660,6 +662,7 @@
<ClInclude Include="SingleplayerSettings.h" />
<ClInclude Include="SpecialFlags.h" />
<ClInclude Include="StdAfx.h" />
<ClInclude Include="StringHelper.h" />
<ClInclude Include="structs.h" />
<ClInclude Include="Tags.h" />
<ClInclude Include="TaskForce.h" />

View file

@ -282,6 +282,9 @@
<ClCompile Include="TextDrawer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="INIMeta.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="MissionEditor.rc">
@ -568,6 +571,12 @@
<ClInclude Include="IniHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="StringHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="INIMeta.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="res\clifftoo.bmp">

View file

@ -0,0 +1,25 @@
#pragma once
#include <afx.h>
#include <vector>
namespace utilities {
static std::vector<CString> split_string(const CString& pSource, TCHAR cSplit = ',')
{
std::vector<CString> 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;
}
}

View file

@ -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);
}

View file

@ -27,6 +27,9 @@
#include <iostream>
#include <afxwin.h>
using std::cout;
using std::endl;
#if !defined(ASSERT)
#define ASSERT(x) if (!(x)) throw("assertion failed");
#endif

View file

@ -102,9 +102,9 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_AFXDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_DEBUG;NOMINMAX;_CONSOLE;_AFXDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)googletest\x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)googletest\x64\include;$(SolutionDir)MissionEditorPackLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeader>Create</PrecompiledHeader>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<LanguageStandard>stdcpp20</LanguageStandard>
@ -133,11 +133,14 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\MissionEditor\IniFile.cpp" />
<ClCompile Include="..\MissionEditor\INIMeta.cpp" />
<ClCompile Include="CIni_Meta_Test.cpp" />
<ClCompile Include="CIni_Test.cpp" />
<ClCompile Include="UnitTest.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\MissionEditor\IniFile.h" />
<ClInclude Include="..\MissionEditor\INIMeta.h" />
<ClInclude Include="StdAfx.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View file

@ -24,6 +24,12 @@
<ClCompile Include="..\MissionEditor\IniFile.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="CIni_Meta_Test.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="..\MissionEditor\INIMeta.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="StdAfx.h">
@ -32,5 +38,8 @@
<ClInclude Include="..\MissionEditor\IniFile.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="..\MissionEditor\INIMeta.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
</Project>