From c7897300c0bfe1a7450d6af09ddc62be32c33330 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 15 Jan 2024 13:10:25 +0200 Subject: [PATCH] Fixed missing translation strings on loading campaign save - campaign header now contains text container and stores campaign texts - map header now contains text container instead of inheriting it - moved text container registration logic to a wraper class - fixed registration of copied text containers (from copied map header) --- lib/CGeneralTextHandler.cpp | 25 +++++++++++++++++++++++++ lib/CGeneralTextHandler.h | 12 ++++++++++++ lib/campaign/CampaignHandler.cpp | 14 +++++++------- lib/campaign/CampaignHandler.h | 4 ++-- lib/campaign/CampaignState.cpp | 5 +++++ lib/campaign/CampaignState.h | 12 +++++++++--- lib/mapping/CMapHeader.cpp | 13 +++---------- lib/mapping/CMapHeader.h | 5 +++-- lib/serializer/CSerializer.h | 2 +- 9 files changed, 67 insertions(+), 25 deletions(-) diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 4c6ba5c49..aa963c965 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -264,11 +264,14 @@ void TextLocalizationContainer::registerStringOverride(const std::string & modCo void TextLocalizationContainer::addSubContainer(const TextLocalizationContainer & container) { + assert(!vstd::contains(subContainers, &container)); subContainers.push_back(&container); } void TextLocalizationContainer::removeSubContainer(const TextLocalizationContainer & container) { + assert(vstd::contains(subContainers, &container)); + subContainers.erase(std::remove(subContainers.begin(), subContainers.end(), &container), subContainers.end()); } @@ -414,6 +417,28 @@ void TextLocalizationContainer::jsonSerialize(JsonNode & dest) const } } +TextContainerRegistrable::TextContainerRegistrable() +{ + VLC->generaltexth->addSubContainer(*this); +} + +TextContainerRegistrable::~TextContainerRegistrable() +{ + VLC->generaltexth->removeSubContainer(*this); +} + +TextContainerRegistrable::TextContainerRegistrable(const TextContainerRegistrable & other) + : TextLocalizationContainer(other) +{ + VLC->generaltexth->addSubContainer(*this); +} + +TextContainerRegistrable::TextContainerRegistrable(TextContainerRegistrable && other) noexcept + :TextLocalizationContainer(other) +{ + VLC->generaltexth->addSubContainer(*this); +} + void CGeneralTextHandler::readToVector(const std::string & sourceID, const std::string & sourceName) { CLegacyConfigParser parser(TextPath::builtin(sourceName)); diff --git a/lib/CGeneralTextHandler.h b/lib/CGeneralTextHandler.h index 980a3c03e..7b57d9fdb 100644 --- a/lib/CGeneralTextHandler.h +++ b/lib/CGeneralTextHandler.h @@ -218,6 +218,18 @@ public: } }; +class DLL_LINKAGE TextContainerRegistrable : public TextLocalizationContainer +{ +public: + TextContainerRegistrable(); + ~TextContainerRegistrable(); + + TextContainerRegistrable(const TextContainerRegistrable & other); + TextContainerRegistrable(TextContainerRegistrable && other) noexcept; + + TextContainerRegistrable& operator=(TextContainerRegistrable b) = delete; +}; + /// Handles all text-related data in game class DLL_LINKAGE CGeneralTextHandler: public TextLocalizationContainer { diff --git a/lib/campaign/CampaignHandler.cpp b/lib/campaign/CampaignHandler.cpp index ad59559df..ae4d424e5 100644 --- a/lib/campaign/CampaignHandler.cpp +++ b/lib/campaign/CampaignHandler.cpp @@ -124,7 +124,7 @@ static std::string convertMapName(std::string input) return input; } -std::string CampaignHandler::readLocalizedString(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier) +std::string CampaignHandler::readLocalizedString(CampaignHeader & target, CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier) { TextIdentifier stringID( "campaign", convertMapName(filename), identifier); @@ -133,7 +133,7 @@ std::string CampaignHandler::readLocalizedString(CBinaryReader & reader, std::st if (input.empty()) return ""; - VLC->generaltexth->registerString(modName, stringID, input); + target.getTexts().registerString(modName, stringID, input); return stringID.get(); } @@ -383,8 +383,8 @@ void CampaignHandler::readHeaderFromMemory( CampaignHeader & ret, CBinaryReader ret.version = static_cast(reader.readUInt32()); ui8 campId = reader.readUInt8() - 1;//change range of it from [1, 20] to [0, 19] ret.loadLegacyData(campId); - ret.name.appendTextID(readLocalizedString(reader, filename, modName, encoding, "name")); - ret.description.appendTextID(readLocalizedString(reader, filename, modName, encoding, "description")); + ret.name.appendTextID(readLocalizedString(ret, reader, filename, modName, encoding, "name")); + ret.description.appendTextID(readLocalizedString(ret, reader, filename, modName, encoding, "description")); if (ret.version > CampaignVersion::RoE) ret.difficultyChoosenByPlayer = reader.readInt8(); else @@ -396,7 +396,7 @@ void CampaignHandler::readHeaderFromMemory( CampaignHeader & ret, CBinaryReader ret.encoding = encoding; } -CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader, const CampaignHeader & header) +CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader, CampaignHeader & header) { auto prologEpilogReader = [&](const std::string & identifier) -> CampaignScenarioPrologEpilog { @@ -410,7 +410,7 @@ CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader ret.prologVideo = CampaignHandler::prologVideoName(index); ret.prologMusic = CampaignHandler::prologMusicName(reader.readUInt8()); ret.prologVoice = isOriginalCampaign ? CampaignHandler::prologVoiceName(index) : AudioPath(); - ret.prologText.appendTextID(readLocalizedString(reader, header.filename, header.modName, header.encoding, identifier)); + ret.prologText.appendTextID(readLocalizedString(header, reader, header.filename, header.modName, header.encoding, identifier)); } return ret; }; @@ -428,7 +428,7 @@ CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader } ret.regionColor = reader.readUInt8(); ret.difficulty = reader.readUInt8(); - ret.regionText.appendTextID(readLocalizedString(reader, header.filename, header.modName, header.encoding, ret.mapName + ".region")); + ret.regionText.appendTextID(readLocalizedString(header, reader, header.filename, header.modName, header.encoding, ret.mapName + ".region")); ret.prolog = prologEpilogReader(ret.mapName + ".prolog"); ret.epilog = prologEpilogReader(ret.mapName + ".epilog"); diff --git a/lib/campaign/CampaignHandler.h b/lib/campaign/CampaignHandler.h index d6e2d0579..e1c54c6e8 100644 --- a/lib/campaign/CampaignHandler.h +++ b/lib/campaign/CampaignHandler.h @@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN class DLL_LINKAGE CampaignHandler { - static std::string readLocalizedString(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier); + static std::string readLocalizedString(CampaignHeader & target, CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier); static void readCampaign(Campaign * target, const std::vector & stream, std::string filename, std::string modName, std::string encoding); @@ -27,7 +27,7 @@ class DLL_LINKAGE CampaignHandler //parsers for original H3C campaigns static void readHeaderFromMemory(CampaignHeader & target, CBinaryReader & reader, std::string filename, std::string modName, std::string encoding); - static CampaignScenario readScenarioFromMemory(CBinaryReader & reader, const CampaignHeader & header); + static CampaignScenario readScenarioFromMemory(CBinaryReader & reader, CampaignHeader & header); static CampaignTravel readScenarioTravelFromMemory(CBinaryReader & reader, CampaignVersion version); /// returns h3c split in parts. 0 = h3c header, 1-end - maps (binary h3m) /// headerOnly - only header will be decompressed, returned vector wont have any maps diff --git a/lib/campaign/CampaignState.cpp b/lib/campaign/CampaignState.cpp index d23ffd314..98623a509 100644 --- a/lib/campaign/CampaignState.cpp +++ b/lib/campaign/CampaignState.cpp @@ -169,6 +169,11 @@ const CampaignRegions & CampaignHeader::getRegions() const return campaignRegions; } +TextContainerRegistrable & CampaignHeader::getTexts() +{ + return textContainer; +} + bool CampaignState::isConquered(CampaignScenarioID whichScenario) const { return vstd::contains(mapsConquered, whichScenario); diff --git a/lib/campaign/CampaignState.h b/lib/campaign/CampaignState.h index cbe133467..37e7d91f3 100644 --- a/lib/campaign/CampaignState.h +++ b/lib/campaign/CampaignState.h @@ -9,9 +9,10 @@ */ #pragma once -#include "../lib/GameConstants.h" -#include "../lib/MetaString.h" -#include "../lib/filesystem/ResourcePath.h" +#include "../GameConstants.h" +#include "../MetaString.h" +#include "../filesystem/ResourcePath.h" +#include "../CGeneralTextHandler.h" #include "CampaignConstants.h" #include "CampaignScenarioPrologEpilog.h" @@ -87,6 +88,8 @@ class DLL_LINKAGE CampaignHeader : public boost::noncopyable void loadLegacyData(ui8 campId); + TextContainerRegistrable textContainer; + public: bool playerSelectedDifficulty() const; bool formatVCMI() const; @@ -99,6 +102,7 @@ public: AudioPath getMusic() const; const CampaignRegions & getRegions() const; + TextContainerRegistrable & getTexts(); template void serialize(Handler &h, const int formatVersion) { @@ -112,6 +116,8 @@ public: h & modName; h & music; h & encoding; + if (formatVersion >= 832) + h & textContainer; } }; diff --git a/lib/mapping/CMapHeader.cpp b/lib/mapping/CMapHeader.cpp index cacfc4c24..1f5f7ee33 100644 --- a/lib/mapping/CMapHeader.cpp +++ b/lib/mapping/CMapHeader.cpp @@ -122,13 +122,9 @@ CMapHeader::CMapHeader() : version(EMapFormat::VCMI), height(72), width(72), setupEvents(); allowedHeroes = VLC->heroh->getDefaultAllowed(); players.resize(PlayerColor::PLAYER_LIMIT_I); - VLC->generaltexth->addSubContainer(*this); } -CMapHeader::~CMapHeader() -{ - VLC->generaltexth->removeSubContainer(*this); -} +CMapHeader::~CMapHeader() = default; ui8 CMapHeader::levels() const { @@ -137,9 +133,6 @@ ui8 CMapHeader::levels() const void CMapHeader::registerMapStrings() { - VLC->generaltexth->removeSubContainer(*this); - VLC->generaltexth->addSubContainer(*this); - //get supported languages. Assuming that translation containing most strings is the base language std::set mapLanguages, mapBaseLanguages; int maxStrings = 0; @@ -193,7 +186,7 @@ void CMapHeader::registerMapStrings() JsonUtils::mergeCopy(data, translations[language]); for(auto & s : data.Struct()) - registerString("map", TextIdentifier(s.first), s.second.String(), language); + texts.registerString("map", TextIdentifier(s.first), s.second.String(), language); } std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized) @@ -203,7 +196,7 @@ std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeade std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized, const std::string & language) { - mapHeader.registerString(modContext, UID, localized, language); + mapHeader.texts.registerString(modContext, UID, localized, language); mapHeader.translations.Struct()[language].Struct()[UID.get()].String() = localized; return UID.get(); } diff --git a/lib/mapping/CMapHeader.h b/lib/mapping/CMapHeader.h index 4ba9d9a0a..324b7aa48 100644 --- a/lib/mapping/CMapHeader.h +++ b/lib/mapping/CMapHeader.h @@ -192,7 +192,7 @@ struct DLL_LINKAGE TriggeredEvent }; /// The map header holds information about loss/victory condition,map format, version, players, height, width,... -class DLL_LINKAGE CMapHeader: public TextLocalizationContainer +class DLL_LINKAGE CMapHeader { void setupEvents(); public: @@ -240,13 +240,14 @@ public: /// translations for map to be transferred over network JsonNode translations; + TextContainerRegistrable texts; void registerMapStrings(); template void serialize(Handler & h, const int Version) { - h & static_cast(*this); + h & texts; h & version; h & mods; h & name; diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 76a372690..58914184c 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN -const ui32 SERIALIZATION_VERSION = 831; +const ui32 SERIALIZATION_VERSION = 832; const ui32 MINIMAL_SERIALIZATION_VERSION = 831; const std::string SAVEGAME_MAGIC = "VCMISVG";