mirror of
https://github.com/electronicarts/CNC_TS_and_RA2_Mission_Editor.git
synced 2025-04-30 09:01:41 -04:00
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:
parent
3e90dca667
commit
93e956e7ac
8 changed files with 164 additions and 50 deletions
|
@ -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) + ")";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Add table
Reference in a new issue