/* * CGeneralTextHandler.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #pragma once #include "filesystem/ResourcePath.h" VCMI_LIB_NAMESPACE_BEGIN class CInputStream; class JsonNode; class JsonSerializeFormat; /// Parser for any text files from H3 class DLL_LINKAGE CLegacyConfigParser { std::string fileEncoding; std::unique_ptr data; char * curr; char * end; /// extracts part of quoted string. std::string extractQuotedPart(); /// extracts quoted string. Any end of lines are ignored, double-quote is considered as "escaping" std::string extractQuotedString(); /// extracts non-quoted string std::string extractNormalString(); /// reads "raw" string without encoding conversion std::string readRawString(); public: /// read one entry from current line. Return ""/0 if end of line reached std::string readString(); float readNumber(); template std::vector readNumArray(size_t size) { std::vector ret; ret.reserve(size); while (size--) ret.push_back((numeric)readNumber()); return ret; } /// returns true if next entry is empty bool isNextEntryEmpty() const; /// end current line bool endLine(); explicit CLegacyConfigParser(const TextPath & URI); }; class CGeneralTextHandler; /// Small wrapper that provides text access API compatible with old code class DLL_LINKAGE LegacyTextContainer { CGeneralTextHandler & owner; std::string basePath; public: LegacyTextContainer(CGeneralTextHandler & owner, std::string basePath); std::string operator [](size_t index) const; }; /// Small wrapper that provides help text access API compatible with old code class DLL_LINKAGE LegacyHelpContainer { CGeneralTextHandler & owner; std::string basePath; public: LegacyHelpContainer(CGeneralTextHandler & owner, std::string basePath); std::pair operator[](size_t index) const; }; class TextIdentifier { std::string identifier; public: const std::string & get() const { return identifier; } TextIdentifier(const char * id): identifier(id) {} TextIdentifier(const std::string & id): identifier(id) {} template TextIdentifier(const std::string & id, size_t index, T... rest): TextIdentifier(id + '.' + std::to_string(index), rest...) {} template TextIdentifier(const std::string & id, const std::string & id2, T... rest): TextIdentifier(id + '.' + id2, rest...) {} }; class DLL_LINKAGE TextLocalizationContainer { protected: struct StringState { /// Human-readable string that was added on registration std::string baseValue; /// Language of base string std::string baseLanguage; /// Translated human-readable string std::string overrideValue; /// Language of the override string std::string overrideLanguage; /// ID of mod that created this string std::string modContext; template void serialize(Handler & h) { h & baseValue; h & baseLanguage; h & modContext; } }; /// map identifier -> localization std::unordered_map stringsLocalizations; std::vector subContainers; /// add selected string to internal storage as high-priority strings void registerStringOverride(const std::string & modContext, const std::string & language, const TextIdentifier & UID, const std::string & localized); std::string getModLanguage(const std::string & modContext); public: /// validates translation of specified language for specified mod /// returns true if localization is valid and complete /// any error messages will be written to log file bool validateTranslation(const std::string & language, const std::string & modContext, JsonNode const & file) const; /// Loads translation from provided json /// Any entries loaded by this will have priority over texts registered normally void loadTranslationOverrides(const std::string & language, const std::string & modContext, JsonNode const & file); // returns true if identifier with such name was registered, even if not translated to current language bool identifierExists(const TextIdentifier & UID) const; /// add selected string to internal storage void registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized); void registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized, const std::string & language); /// returns translated version of a string that can be displayed to user template std::string translate(std::string arg1, Args ... args) const { TextIdentifier id(arg1, args ...); return deserialize(id); } /// converts identifier into user-readable string const std::string & deserialize(const TextIdentifier & identifier) const; /// Debug method, returns all currently stored texts /// Format: [mod ID][string ID] -> human-readable text void exportAllTexts(std::map> & storage) const; /// Add or override subcontainer which can store identifiers void addSubContainer(const TextLocalizationContainer & container); /// Remove subcontainer with give name void removeSubContainer(const TextLocalizationContainer & container); void jsonSerialize(JsonNode & dest) const; template void serialize(Handler & h) { std::string key; auto sz = stringsLocalizations.size(); h & sz; if(h.saving) { for(auto s : stringsLocalizations) { key = s.first; h & key; h & s.second; } } else { for(size_t i = 0; i < sz; ++i) { h & key; h & stringsLocalizations[key]; } } } }; class DLL_LINKAGE TextContainerRegistrable : public TextLocalizationContainer { public: TextContainerRegistrable(); ~TextContainerRegistrable(); TextContainerRegistrable(const TextContainerRegistrable & other); TextContainerRegistrable(TextContainerRegistrable && other) noexcept; TextContainerRegistrable& operator=(const TextContainerRegistrable & b) = default; }; /// Handles all text-related data in game class DLL_LINKAGE CGeneralTextHandler: public TextLocalizationContainer { void readToVector(const std::string & sourceID, const std::string & sourceName); /// number of scenarios in specific campaign. TODO: move to a better location std::vector scenariosCountPerCampaign; public: LegacyTextContainer allTexts; LegacyTextContainer arraytxt; LegacyTextContainer primarySkillNames; LegacyTextContainer jktexts; LegacyTextContainer heroscrn; LegacyTextContainer overview;//text for Kingdom Overview window LegacyTextContainer colors; //names of player colors ("red",...) LegacyTextContainer capColors; //names of player colors with first letter capitalized ("Red",...) LegacyTextContainer turnDurations; //turn durations for pregame (1 Minute ... Unlimited) //towns LegacyTextContainer tcommands, hcommands, fcommands; //texts for town screen, town hall screen and fort screen LegacyTextContainer tavernInfo; LegacyTextContainer tavernRumors; LegacyTextContainer qeModCommands; LegacyHelpContainer zelp; LegacyTextContainer lossCondtions; LegacyTextContainer victoryConditions; //objects LegacyTextContainer advobtxt; LegacyTextContainer restypes; //names of resources LegacyTextContainer randsign; LegacyTextContainer seerEmpty; LegacyTextContainer seerNames; LegacyTextContainer tentColors; //sec skills LegacyTextContainer levels; //commanders LegacyTextContainer znpc00; //more or less useful content of that file std::vector findStringsWithPrefix(const std::string & prefix); int32_t pluralText(int32_t textIndex, int32_t count) const; size_t getCampaignLength(size_t campaignID) const; CGeneralTextHandler(); CGeneralTextHandler(const CGeneralTextHandler&) = delete; CGeneralTextHandler operator=(const CGeneralTextHandler&) = delete; /// Attempts to detect encoding & language of H3 files static void detectInstallParameters(); /// Returns name of language preferred by user static std::string getPreferredLanguage(); /// Returns name of language of Heroes III text files static std::string getInstalledLanguage(); /// Returns name of encoding of Heroes III text files static std::string getInstalledEncoding(); }; VCMI_LIB_NAMESPACE_END