diff --git a/MissionEditor/Building.cpp b/MissionEditor/Building.cpp index 7eae36c..890d6d0 100644 --- a/MissionEditor/Building.cpp +++ b/MissionEditor/Building.cpp @@ -27,6 +27,7 @@ #include "mapdata.h" #include "variables.h" #include "inlines.h" +#include "IniMega.h" #ifdef _DEBUG @@ -157,7 +158,8 @@ BOOL CBuilding::OnInitDialog() if (upgradecount > 0) { auto updatePowerupItems = [=](const CIniFile& ini) { - for (auto const& [seq, unitname] : ini["BuildingTypes"]) { + auto const& rules = IniMegaFile::GetRules(); + for (auto const& [seq, unitname] : rules.GetSection("BuildingTypes")) { auto const& targetBldID = ini.GetString(unitname, "PowersUpBuilding"); if (targetBldID == m_type) { auto const desc = unitname + " (" + GetName(unitname) + ")"; diff --git a/MissionEditor/FinalSunDlg.cpp b/MissionEditor/FinalSunDlg.cpp index 7450189..5552357 100644 --- a/MissionEditor/FinalSunDlg.cpp +++ b/MissionEditor/FinalSunDlg.cpp @@ -1281,7 +1281,8 @@ void CFinalSunDlg::SaveMap(CString FileName_) char c[50]; for (auto e = 0; e < sec.Size(); e++) { auto const& [key, value] = sec.Nth(e); - fi = key; + // restore += format + fi = key[0] != '+' ? key : '+'; fi += "="; fi += value; fi += "\n"; diff --git a/MissionEditor/INIMeta.h b/MissionEditor/INIMeta.h index 6339d69..0236c74 100644 --- a/MissionEditor/INIMeta.h +++ b/MissionEditor/INIMeta.h @@ -129,6 +129,15 @@ public: return Iterator(m_section, m_source.end(), m_source.end(), secItEnd, secItEnd); } + bool HasValue(const CString& expected) const { + for (auto const& [_, val] : *this) { + if (val == expected) { + return true; + } + } + return false; + } + private: // attention: beginning iter static std::pair< KvIter, KvIter> acquireNextKvGroup(const CString& section, GroupIter& beg, const GroupIter end) { diff --git a/MissionEditor/IniFile.cpp b/MissionEditor/IniFile.cpp index 7e160bd..c37f36c 100644 --- a/MissionEditor/IniFile.cpp +++ b/MissionEditor/IniFile.cpp @@ -24,10 +24,12 @@ #include "stdafx.h" #include "IniFile.h" +#include "Helpers.h" #include #include #include - +#include +#include #ifdef _DEBUG @@ -92,64 +94,121 @@ CIniFileSection::~CIniFileSection() value_pairs.clear(); }; +bool isSectionRegistry(const CString& secName) +{ + static std::unordered_set registryNames = { + "BuildingTypes", + "InfantryTypes", + "AircraftTypes", + "VehicleTypes", + "Animations", + }; + + auto const nameView = std::string_view(secName.GetString(), secName.GetLength()); + return registryNames.find(nameView) != registryNames.end(); +} + WORD CIniFile::InsertFile(const CString& filename, const char* Section, BOOL bNoSpaces) { return InsertFile(std::string(filename.GetString()), Section, bNoSpaces); } -WORD CIniFile::InsertFile(const std::string& filename, const char* Section, BOOL bNoSpaces) +WORD CIniFile::InsertFile(const std::string& filename, const char* pSectionSpecified, BOOL bNoSpaces) { - if (filename.size() == 0) + if (filename.size() == 0) { return 1; + } fstream file; file.open(filename, ios::in); - if (!file.good()) + if (!file.good()) { return 2; - + } //char cSec[256]; //char cLine[4096]; //memset(cSec, 0, 256); //memset(cLine, 0, 4096); - CString cSec; - std::string cLine; + CString curSecParsed; + std::string curLineParsed; const auto npos = std::string::npos; +#if 0 + std::map>> registryMap; +#endif + std::set registryValues; + while (!file.eof()) { - std::getline(file, cLine); + std::getline(file, curLineParsed); // strip to left side of newline or comment - cLine.erase(std::find_if(cLine.begin(), cLine.end(), [](const char c) { return c == '\r' || c == '\n' || c == ';'; }), cLine.end()); + curLineParsed.erase(std::find_if(curLineParsed.begin(), curLineParsed.end(), [](const char c) { return c == '\r' || c == '\n' || c == ';'; }), curLineParsed.end()); - const auto openBracket = cLine.find('['); - const auto closeBracket = cLine.find(']'); - const auto equals = cLine.find('='); + const auto openBracketPos = curLineParsed.find('['); + const auto closeBracketPos = curLineParsed.find(']'); + const auto equalPos = curLineParsed.find('='); - if (openBracket != npos && closeBracket != npos && openBracket < closeBracket && (equals == npos || equals > openBracket)) { - if ((Section != nullptr) && cSec == Section) - return 0; // the section we want to insert is finished + if (openBracketPos != npos && closeBracketPos != npos && openBracketPos < closeBracketPos && (equalPos == npos || equalPos > openBracketPos)) { + if ((pSectionSpecified != nullptr) && curSecParsed == pSectionSpecified) { + break; // the section we want to insert is finished + } - cSec = cLine.substr(openBracket + 1, closeBracket - openBracket - 1).c_str(); - } else if (equals != npos && !cSec.IsEmpty()) { - if (Section == NULL || cSec == Section) { + curSecParsed = curLineParsed.substr(openBracketPos + 1, closeBracketPos - openBracketPos - 1).c_str(); + registryValues.clear(); + continue; + } + + if (equalPos != npos && !curSecParsed.IsEmpty()) { + if (pSectionSpecified == NULL || curSecParsed == pSectionSpecified) { // a value is set and we have a valid current section! - CString name = cLine.substr(0, equals).c_str(); - CString value = cLine.substr(equals + 1, cLine.size() - equals - 1).c_str(); + CString keyName = curLineParsed.substr(0, equalPos).c_str(); + CString value = curLineParsed.substr(equalPos + 1, curLineParsed.size() - equalPos - 1).c_str(); + auto& curSectionMap = sections[curSecParsed]; if (bNoSpaces) { - name.Trim(); + keyName.Trim(); value.Trim(); } - sections[cSec].SetString(name, value); + + if (isSectionRegistry(curSecParsed) && IsNumeric(keyName)) { + auto const [_, inserted] = registryValues.insert(value); + // WW's duplicated record, skip + if (!inserted) { + continue; + } + } + + // this is a duplicated number, and already added + // so we will append in the end of the list later +#if 0 + if (IsNumeric(keyName) && curSectionMap.Exists(keyName)) { + registryMap[curSecParsed].push_back({ keyName, value }); + } +#endif + + // special handling for += + if (keyName == '+') { + keyName += value; + } + + curSectionMap.SetString(keyName, value); } } } + // now we append all items to the list +#if 0 + for (auto const& [secName, registry] : registryMap) { + auto& secMap = sections[secName]; + for (auto const& [key, value] : registry) { + secMap.SetString("auto" + key, value); + } + } +#endif file.close(); @@ -183,7 +242,12 @@ BOOL CIniFile::SaveFile(const std::string& Filename) const for (auto const& sec : sections) { file << "[" << sec.first << "]" << endl; for (auto const& pair : sec.second) { - file << pair.first << "=" << pair.second << endl; + auto keyName = pair.first; + // restore += + if (keyName[0] == '+') { + keyName = '+'; + } + file << keyName << "=" << pair.second << endl; } file << endl; } diff --git a/MissionEditor/Loading.cpp b/MissionEditor/Loading.cpp index b907716..f60b268 100644 --- a/MissionEditor/Loading.cpp +++ b/MissionEditor/Loading.cpp @@ -854,7 +854,8 @@ void CLoading::InitPics(CProgressCtrl* prog) // new: Prepare building terrain information: - for (auto const& [seq, id] : rules.GetSection("BuildingTypes")) { + auto const& rulesGroup = IniMegaFile::GetRules(); + for (auto const& [seq, id] : rulesGroup.GetSection("BuildingTypes")) { PrepareUnitGraphic(id); } ms.dwLength = sizeof(MEMORYSTATUS); @@ -1082,9 +1083,10 @@ void CLoading::InitSHPs(CProgressCtrl* prog) if (!theApp.m_Options.bDoNotLoadBuildingGraphics) { - auto const& sec = rules.GetSection("BuildingTypes"); - for (i = 0; i < sec.Size(); i++) { - LoadUnitGraphic(sec.Nth(i).second); + auto const& rulesGroup = IniMegaFile::GetRules(); + auto const& sec = rulesGroup.GetSection("BuildingTypes"); + for (auto const& [_, id] : sec) { + LoadUnitGraphic(id); } } @@ -4127,7 +4129,11 @@ void CLoading::PrepareUnitGraphic(const CString& lpUnittype) WORD wStep = 1; // step is 1 for infantry, buildings, etc, and for shp vehicles it specifies the step rate between every direction WORD wStartWalkFrame = 0; // for examply cyborg reaper has another walk starting frame int iTurretOffset = 0; // used for centering y pos of turret (if existing) - BOOL bStructure = rules["BuildingTypes"].HasValue(lpUnittype); // is this a structure? + + auto const& rules = IniMegaFile::GetRules(); + + + BOOL bStructure = rules.GetSection("BuildingTypes").HasValue(lpUnittype); // is this a structure? // make sure we only use it for buildings now if (!bStructure) { @@ -4166,7 +4172,7 @@ void CLoading::PrepareUnitGraphic(const CString& lpUnittype) } const CString& image = _art_image; - const auto& rulesSection = rules[lpUnittype]; + const auto& rulesSection = rules.GetSection(lpUnittype); const auto& artSection = art[image]; // is it a shp graphic? diff --git a/MissionEditor/MapData.cpp b/MissionEditor/MapData.cpp index 8594466..5e38938 100644 --- a/MissionEditor/MapData.cpp +++ b/MissionEditor/MapData.cpp @@ -32,6 +32,7 @@ #include "progressdlg.h" #include "Structs.h" #include "Tube.h" +#include "IniMega.h" #ifdef _DEBUG #undef THIS_FILE @@ -3525,15 +3526,15 @@ BuildingFoundation getBuildingFoundation(const CString& artId) { // TODO: simplify this function, remove duplicated codes void CMapData::UpdateBuildingInfo(const CString* lpUnitType) { - CIniFile& ini = GetIniFile(); + auto const& rulesGroup = IniMegaFile::GetRules(); if (!lpUnitType) { memset(buildinginfo, 0, buildingInfoCapacity * sizeof(BUILDING_INFO)); - for (auto const& [seq, id] : rules.GetSection("BuildingTypes")) { + for (auto const& [seq, id] : rulesGroup.GetSection("BuildingTypes")) { auto const& type = id; - auto artname = rules.GetStringOr(type, "Image", type); - artname = ini.GetStringOr(type, "Image", artname); + auto artname = rulesGroup.GetStringOr(type, "Image", type); + artname = rulesGroup.GetStringOr(type, "Image", artname); auto const foundation = getBuildingFoundation(artname); @@ -3588,9 +3589,9 @@ void CMapData::UpdateBuildingInfo(const CString* lpUnitType) } - for (auto const& [seq, id] : rules.GetSection("BuildingTypes")) { + for (auto const& [seq, id] : rulesGroup.GetSection("BuildingTypes")) { auto const& type = id; - auto artname = ini.GetStringOr(type, "Image", type); + auto artname = rulesGroup.GetStringOr(type, "Image", type); auto const foundation = getBuildingFoundation(artname); @@ -3624,8 +3625,8 @@ void CMapData::UpdateBuildingInfo(const CString* lpUnitType) // only for specific building -> faster auto const& type = *lpUnitType; - auto artname = rules.GetStringOr(type, "Image", type); - artname = ini.GetStringOr(type, "Image", artname); + auto artname = rulesGroup.GetStringOr(type, "Image", type); + artname = rulesGroup.GetStringOr(type, "Image", artname); auto const foundation = getBuildingFoundation(artname); diff --git a/MissionEditor/functions.cpp b/MissionEditor/functions.cpp index 4becb27..231952f 100644 --- a/MissionEditor/functions.cpp +++ b/MissionEditor/functions.cpp @@ -657,29 +657,30 @@ void listSpecifcTechnoTypes(CComboBox& cb, const CString& sectionName, bool clea if (clear) { while (cb.DeleteString(0) != CB_ERR); } + auto const& rules = IniMegaFile::GetRules(); auto const& sec = rules.GetSection(sectionName); - for (auto idx = 0; idx < sec.Size(); ++idx) { - char idxNum[50]; - _itoa_s(idx, idxNum, 10); - CString record = idxNum; - - auto const& kvPair = sec.Nth(idx); + auto idx = 0; + for (auto& [_, id] : sec) { + CString record; if (useIniName) { - record = kvPair.second; - } - else { + record = id; + } else { + char idxNum[50]; + _itoa_s(idx, idxNum, 10); + record = idxNum; record += ' '; - record += kvPair.second; + record += id; } record += " "; - CString translated = Map->GetUnitName(kvPair.second); + CString translated = Map->GetUnitName(id); //if(t!="MISSING") { record += translated; cb.AddString(record); } + ++idx; } } diff --git a/UnitTest/CIni_Test.cpp b/UnitTest/CIni_Test.cpp index c2dfa04..e3ff204 100644 --- a/UnitTest/CIni_Test.cpp +++ b/UnitTest/CIni_Test.cpp @@ -275,4 +275,34 @@ TEST(CIniFileClass, IniLowerBoundInsertTest) { EXPECT_EQ(987654, file.GetInteger("Waypoints", "11")); EXPECT_EQ("987654", file["Waypoints"].Nth(pSec->Size() - 1).second); EXPECT_EQ("159356", file["Waypoints"].Nth(pSec->Size() - 2).second); +} + + +TEST(CIniFileClass, IniRegistryTest) { + auto const fileName = "test.ini"; + IniTestHelper helper(fileName, R"( +[BuildingTypes] +0=GAPOWR +1=NAPOWR +2=GACNST +5=NACNST +6=GAPOWR +6=NAFAKE + + +)"); + + CIniFile file; + ASSERT_EQ(file.LoadFile(std::string(fileName)), 0); + + EXPECT_EQ("NAFAKE", file.GetString("BuildingTypes", "6")); + + auto const& sec = file.GetSection("BuildingTypes"); + EXPECT_EQ(5, sec.Size()); + + EXPECT_EQ("GAPOWR", sec.Nth(0).second); + EXPECT_EQ("NAPOWR", sec.Nth(1).second); + EXPECT_EQ("GACNST", sec.Nth(2).second); + EXPECT_EQ("NACNST", sec.Nth(3).second); + EXPECT_EQ("NAFAKE", sec.Nth(4).second); } \ No newline at end of file