Enhance:ini registry sequence (#121)

* '+=' supported .
* introduced registry list value deduplication .
* TechnoType listing now parse data both from ini and map .
* added UT case .
This commit is contained in:
Zero Fanker 2024-12-01 14:09:30 -05:00 committed by GitHub
parent 3e90dca667
commit 93e956e7ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 164 additions and 50 deletions

View file

@ -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) + ")";

View file

@ -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";

View file

@ -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) {

View file

@ -24,10 +24,12 @@
#include "stdafx.h"
#include "IniFile.h"
#include "Helpers.h"
#include <string>
#include <algorithm>
#include <stdexcept>
#include <set>
#include <unordered_set>
#ifdef _DEBUG
@ -92,64 +94,121 @@ CIniFileSection::~CIniFileSection()
value_pairs.clear();
};
bool isSectionRegistry(const CString& secName)
{
static std::unordered_set<std::string_view> 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<CString, std::list<std::pair<CString, CString>>> registryMap;
#endif
std::set<CString> 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;
}

View file

@ -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?

View file

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

View file

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

View file

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