2017-07-13 10:26:03 +02:00
/*
* CGeneralTextHandler . cpp , 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
*
*/
2011-12-14 00:23:17 +03:00
# include "StdInc.h"
2007-07-08 20:28:08 +03:00
# include "CGeneralTextHandler.h"
2011-12-14 00:23:17 +03:00
2013-07-28 17:49:50 +03:00
# include "filesystem/Filesystem.h"
2013-10-26 00:45:14 +03:00
# include "CConfigHandler.h"
2023-03-15 21:34:29 +02:00
# include "GameSettings.h"
2022-12-27 23:17:41 +02:00
# include "mapObjects/CQuest.h"
2023-07-30 19:12:25 +02:00
# include "modding/CModHandler.h"
2013-04-26 00:50:55 +03:00
# include "VCMI_Lib.h"
2023-02-09 22:26:25 +02:00
# include "Languages.h"
2023-02-12 23:00:56 +02:00
# include "TextOperations.h"
2007-07-08 20:28:08 +03:00
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2023-01-16 23:20:24 +02:00
/// Detects language and encoding of H3 text files based on matching against pregenerated footprints of H3 file
2023-02-09 15:03:49 +02:00
void CGeneralTextHandler : : detectInstallParameters ( )
2013-10-26 00:45:14 +03:00
{
2023-02-09 22:26:25 +02:00
using LanguageFootprint = std : : array < double , 16 > ;
2023-03-06 18:39:32 +02:00
static const std : : array < LanguageFootprint , 7 > knownFootprints =
2023-02-09 22:26:25 +02:00
{ {
2023-03-06 18:39:32 +02:00
{ { 0.1602 , 0.0000 , 0.0357 , 0.0054 , 0.0038 , 0.0017 , 0.0077 , 0.0214 , 0.0000 , 0.0000 , 0.1264 , 0.1947 , 0.2012 , 0.1406 , 0.0480 , 0.0532 } } ,
2023-02-09 22:26:25 +02:00
{ { 0.0559 , 0.0000 , 0.1983 , 0.0051 , 0.0222 , 0.0183 , 0.4596 , 0.2405 , 0.0000 , 0.0000 , 0.0000 , 0.0000 , 0.0000 , 0.0000 , 0.0000 , 0.0000 } } ,
{ { 0.0493 , 0.0000 , 0.1926 , 0.0047 , 0.0230 , 0.0121 , 0.4133 , 0.2780 , 0.0002 , 0.0000 , 0.0000 , 0.0000 , 0.0000 , 0.0000 , 0.0259 , 0.0008 } } ,
{ { 0.0534 , 0.0000 , 0.1705 , 0.0047 , 0.0418 , 0.0208 , 0.4775 , 0.2191 , 0.0001 , 0.0000 , 0.0000 , 0.0000 , 0.0000 , 0.0005 , 0.0036 , 0.0080 } } ,
{ { 0.0534 , 0.0000 , 0.1701 , 0.0067 , 0.0157 , 0.0133 , 0.4328 , 0.2540 , 0.0001 , 0.0043 , 0.0000 , 0.0244 , 0.0000 , 0.0000 , 0.0181 , 0.0071 } } ,
{ { 0.0548 , 0.0000 , 0.1744 , 0.0061 , 0.0031 , 0.0009 , 0.0046 , 0.0136 , 0.0000 , 0.0004 , 0.0000 , 0.0000 , 0.0227 , 0.0061 , 0.4882 , 0.2252 } } ,
{ { 0.0559 , 0.0000 , 0.1807 , 0.0059 , 0.0036 , 0.0013 , 0.0046 , 0.0134 , 0.0000 , 0.0004 , 0.0000 , 0.0487 , 0.0209 , 0.0060 , 0.4615 , 0.1972 } } ,
} } ;
2023-03-06 18:39:32 +02:00
static const std : : array < std : : string , 7 > knownLanguages =
2023-02-09 22:26:25 +02:00
{ {
2023-03-06 18:39:32 +02:00
" chinese " ,
2023-02-09 22:26:25 +02:00
" english " ,
" french " ,
" german " ,
" polish " ,
" russian " ,
" ukrainian "
} } ;
2023-01-09 00:24:11 +02:00
2023-09-04 12:03:15 +02:00
if ( ! CResourceHandler : : get ( " core " ) - > existsResource ( TextPath : : builtin ( " DATA/GENRLTXT.TXT " ) ) )
2023-03-26 22:20:35 +02:00
{
Settings language = settings . write [ " session " ] [ " language " ] ;
language - > String ( ) = " english " ;
Settings confidence = settings . write [ " session " ] [ " languageDeviation " ] ;
confidence - > Float ( ) = 1.0 ;
Settings encoding = settings . write [ " session " ] [ " encoding " ] ;
encoding - > String ( ) = Languages : : getLanguageOptions ( " english " ) . encoding ;
return ;
}
2023-01-09 00:24:11 +02:00
// load file that will be used for footprint generation
// this is one of the most text-heavy files in game and consists solely from translated texts
2023-09-04 12:03:15 +02:00
auto resource = CResourceHandler : : get ( " core " ) - > load ( TextPath : : builtin ( " DATA/GENRLTXT.TXT " ) ) ;
2023-01-09 00:24:11 +02:00
2023-02-09 15:03:49 +02:00
std : : array < size_t , 256 > charCount { } ;
std : : array < double , 16 > footprint { } ;
2023-02-09 22:26:25 +02:00
std : : array < double , 6 > deviations { } ;
2023-01-09 00:24:11 +02:00
auto data = resource - > readAll ( ) ;
// compute how often each character occurs in input file
2023-01-15 23:55:24 +02:00
for ( si64 i = 0 ; i < data . second ; + + i )
2023-01-09 00:24:11 +02:00
charCount [ data . first [ i ] ] + = 1 ;
// and convert computed data into weights
// to reduce amount of data, group footprint data into 16-char blocks.
// While this will reduce precision, it should not affect output
// since we expect only tiny differences compared to reference footprints
for ( size_t i = 0 ; i < 256 ; + + i )
2023-02-09 15:03:49 +02:00
footprint [ i / 16 ] + = static_cast < double > ( charCount [ i ] ) / data . second ;
2023-01-09 00:24:11 +02:00
logGlobal - > debug ( " Language footprint: %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f " ,
footprint [ 0 ] , footprint [ 1 ] , footprint [ 2 ] , footprint [ 3 ] , footprint [ 4 ] , footprint [ 5 ] , footprint [ 6 ] , footprint [ 7 ] ,
footprint [ 8 ] , footprint [ 9 ] , footprint [ 10 ] , footprint [ 11 ] , footprint [ 12 ] , footprint [ 13 ] , footprint [ 14 ] , footprint [ 15 ]
) ;
for ( size_t i = 0 ; i < deviations . size ( ) ; + + i )
{
for ( size_t j = 0 ; j < footprint . size ( ) ; + + j )
2023-02-09 22:26:25 +02:00
deviations [ i ] + = std : : abs ( ( footprint [ j ] - knownFootprints [ i ] [ j ] ) ) ;
2023-01-09 00:24:11 +02:00
}
size_t bestIndex = boost : : range : : min_element ( deviations ) - deviations . begin ( ) ;
for ( size_t i = 0 ; i < deviations . size ( ) ; + + i )
2023-02-09 22:26:25 +02:00
logGlobal - > debug ( " Comparing to %s: %f " , knownLanguages [ i ] , deviations [ i ] ) ;
2023-01-09 00:24:11 +02:00
2023-02-09 15:03:49 +02:00
Settings language = settings . write [ " session " ] [ " language " ] ;
2023-02-09 22:26:25 +02:00
language - > String ( ) = knownLanguages [ bestIndex ] ;
2023-02-09 15:03:49 +02:00
2023-03-11 00:57:55 +02:00
Settings confidence = settings . write [ " session " ] [ " languageDeviation " ] ;
confidence - > Float ( ) = deviations [ bestIndex ] ;
2023-01-16 23:20:24 +02:00
Settings encoding = settings . write [ " session " ] [ " encoding " ] ;
2023-02-09 22:26:25 +02:00
encoding - > String ( ) = Languages : : getLanguageOptions ( knownLanguages [ bestIndex ] ) . encoding ;
2013-10-26 00:45:14 +03:00
}
2012-08-25 11:44:51 +03:00
//Helper for string -> float conversion
class LocaleWithComma : public std : : numpunct < char >
{
protected :
2015-10-12 15:47:10 +02:00
char do_decimal_point ( ) const override
2012-08-25 11:44:51 +03:00
{
return ' , ' ;
}
} ;
2023-09-01 23:26:14 +02:00
CLegacyConfigParser : : CLegacyConfigParser ( const TextPath & resource )
2008-12-22 19:48:41 +02:00
{
2023-03-05 21:50:38 +02:00
auto input = CResourceHandler : : get ( ) - > load ( resource ) ;
std : : string modName = VLC - > modh - > findResourceOrigin ( resource ) ;
std : : string language = VLC - > modh - > getModLanguage ( modName ) ;
fileEncoding = Languages : : getLanguageOptions ( language ) . encoding ;
2009-07-03 22:57:14 +03:00
2012-08-25 11:44:51 +03:00
data . reset ( new char [ input - > getSize ( ) ] ) ;
2023-02-09 15:03:49 +02:00
input - > read ( reinterpret_cast < uint8_t * > ( data . get ( ) ) , input - > getSize ( ) ) ;
2012-08-01 15:02:54 +03:00
2012-08-25 11:44:51 +03:00
curr = data . get ( ) ;
end = curr + input - > getSize ( ) ;
2012-08-01 15:02:54 +03:00
}
2012-08-25 11:44:51 +03:00
std : : string CLegacyConfigParser : : extractQuotedPart ( )
2008-11-28 03:36:34 +02:00
{
2012-08-25 11:44:51 +03:00
assert ( * curr = = ' \" ' ) ;
2008-11-28 03:36:34 +02:00
2012-08-25 11:44:51 +03:00
curr + + ; // skip quote
char * begin = curr ;
2008-11-28 03:36:34 +02:00
2013-01-26 22:39:54 +03:00
while ( curr ! = end & & * curr ! = ' \" ' & & * curr ! = ' \t ' )
2012-08-25 11:44:51 +03:00
curr + + ;
2008-11-28 03:36:34 +02:00
2012-08-25 11:44:51 +03:00
return std : : string ( begin , curr + + ) ; //increment curr to close quote
}
2008-12-22 19:48:41 +02:00
2012-08-25 11:44:51 +03:00
std : : string CLegacyConfigParser : : extractQuotedString ( )
{
assert ( * curr = = ' \" ' ) ;
2008-12-22 19:48:41 +02:00
2012-08-25 11:44:51 +03:00
std : : string ret ;
while ( true )
2008-12-22 19:48:41 +02:00
{
2012-08-25 11:44:51 +03:00
ret + = extractQuotedPart ( ) ;
2008-12-22 19:48:41 +02:00
2015-09-12 22:03:10 +02:00
// double quote - add it to string and continue quoted part
if ( curr < end & & * curr = = ' \" ' )
2013-01-26 22:39:54 +03:00
{
2012-08-25 11:44:51 +03:00
ret + = ' \" ' ;
2013-01-26 22:39:54 +03:00
}
2015-09-12 22:03:10 +02:00
//extract normal part
else if ( curr < end & & * curr ! = ' \t ' & & * curr ! = ' \r ' )
{
char * begin = curr ;
while ( curr < end & & * curr ! = ' \t ' & & * curr ! = ' \r ' & & * curr ! = ' \" ' ) //find end of string or next quoted part start
2016-08-12 11:10:27 +02:00
curr + + ;
2015-09-12 22:03:10 +02:00
ret + = std : : string ( begin , curr ) ;
2016-08-12 11:10:27 +02:00
2015-09-12 22:03:10 +02:00
if ( curr > = end | | * curr ! = ' \" ' )
return ret ;
2016-08-12 11:10:27 +02:00
}
2012-08-25 11:44:51 +03:00
else // end of string
return ret ;
2008-12-22 19:48:41 +02:00
}
2012-08-25 11:44:51 +03:00
}
std : : string CLegacyConfigParser : : extractNormalString ( )
{
char * begin = curr ;
while ( curr < end & & * curr ! = ' \t ' & & * curr ! = ' \r ' ) //find end of string
curr + + ;
return std : : string ( begin , curr ) ;
}
2013-10-26 00:45:14 +03:00
std : : string CLegacyConfigParser : : readRawString ( )
2012-08-25 11:44:51 +03:00
{
if ( curr > = end | | * curr = = ' \n ' )
return " " ;
std : : string ret ;
if ( * curr = = ' \" ' )
ret = extractQuotedString ( ) ; // quoted text - find closing quote
else
ret = extractNormalString ( ) ; //string without quotes - copy till \t or \r
2018-04-19 15:12:18 +02:00
curr + + ;
2012-08-25 11:44:51 +03:00
return ret ;
}
2013-10-26 00:45:14 +03:00
std : : string CLegacyConfigParser : : readString ( )
{
// do not convert strings that are already in ASCII - this will only slow down loading process
std : : string str = readRawString ( ) ;
2023-02-12 23:52:35 +02:00
if ( TextOperations : : isValidASCII ( str ) )
2013-10-26 00:45:14 +03:00
return str ;
2023-02-24 16:15:45 +02:00
return TextOperations : : toUnicode ( str , fileEncoding ) ;
2013-10-26 00:45:14 +03:00
}
2012-08-25 11:44:51 +03:00
float CLegacyConfigParser : : readNumber ( )
{
2013-10-26 00:45:14 +03:00
std : : string input = readRawString ( ) ;
2012-08-25 11:44:51 +03:00
std : : istringstream stream ( input ) ;
2017-07-16 11:58:05 +02:00
if ( input . find ( ' , ' ) ! = std : : string : : npos ) // code to handle conversion with comma as decimal separator
stream . imbue ( std : : locale ( std : : locale ( ) , new LocaleWithComma ( ) ) ) ;
2012-08-25 11:44:51 +03:00
2013-06-09 13:09:28 +03:00
float result ;
2012-08-25 11:44:51 +03:00
if ( ! ( stream > > result ) )
return 0 ;
return result ;
}
2008-12-22 19:48:41 +02:00
2013-06-23 22:35:54 +03:00
bool CLegacyConfigParser : : isNextEntryEmpty ( ) const
2012-08-25 11:44:51 +03:00
{
2012-09-26 16:13:39 +03:00
char * nextSymbol = curr ;
while ( nextSymbol < end & & * nextSymbol = = ' ' )
nextSymbol + + ; //find next meaningfull symbol
return nextSymbol > = end | | * nextSymbol = = ' \n ' | | * nextSymbol = = ' \r ' | | * nextSymbol = = ' \t ' ;
2012-08-25 11:44:51 +03:00
}
bool CLegacyConfigParser : : endLine ( )
{
while ( curr < end & & * curr ! = ' \n ' )
readString ( ) ;
2018-04-19 15:12:18 +02:00
curr + + ;
2012-08-25 11:44:51 +03:00
return curr < end ;
}
2023-03-13 23:26:44 +02:00
void CGeneralTextHandler : : readToVector ( const std : : string & sourceID , const std : : string & sourceName )
2012-08-25 11:44:51 +03:00
{
2023-09-01 23:26:14 +02:00
CLegacyConfigParser parser ( TextPath : : builtin ( sourceName ) ) ;
2022-12-27 22:19:05 +02:00
size_t index = 0 ;
2012-08-25 11:44:51 +03:00
do
2008-12-22 19:48:41 +02:00
{
2023-02-09 15:03:49 +02:00
registerString ( " core " , { sourceID , index } , parser . readString ( ) ) ;
2022-12-27 22:19:05 +02:00
index + = 1 ;
2008-12-22 19:48:41 +02:00
}
2012-08-25 11:44:51 +03:00
while ( parser . endLine ( ) ) ;
}
2008-12-22 19:48:41 +02:00
2022-12-30 19:09:19 +02:00
const std : : string & CGeneralTextHandler : : deserialize ( const TextIdentifier & identifier ) const
2022-12-27 22:19:05 +02:00
{
2023-02-09 15:03:49 +02:00
if ( stringsLocalizations . count ( identifier . get ( ) ) = = 0 )
{
logGlobal - > error ( " Unable to find localization for string '%s' " , identifier . get ( ) ) ;
return identifier . get ( ) ;
}
2023-01-16 23:20:24 +02:00
2023-03-26 22:20:35 +02:00
const auto & entry = stringsLocalizations . at ( identifier . get ( ) ) ;
2023-01-16 23:20:24 +02:00
2023-02-09 15:03:49 +02:00
if ( ! entry . overrideValue . empty ( ) )
return entry . overrideValue ;
return entry . baseValue ;
2022-12-27 22:19:05 +02:00
}
2023-02-09 15:03:49 +02:00
void CGeneralTextHandler : : registerString ( const std : : string & modContext , const TextIdentifier & UID , const std : : string & localized )
2022-12-27 22:19:05 +02:00
{
2023-03-06 13:29:19 +02:00
assert ( ! modContext . empty ( ) ) ;
assert ( ! getModLanguage ( modContext ) . empty ( ) ) ;
2023-02-09 15:03:49 +02:00
assert ( UID . get ( ) . find ( " .. " ) = = std : : string : : npos ) ; // invalid identifier - there is section that was evaluated to empty string
2023-02-25 17:44:15 +02:00
//assert(stringsLocalizations.count(UID.get()) == 0); // registering already registered string?
2023-03-06 13:29:19 +02:00
if ( stringsLocalizations . count ( UID . get ( ) ) > 0 )
2023-02-25 17:44:15 +02:00
{
2023-03-06 13:29:19 +02:00
auto & value = stringsLocalizations [ UID . get ( ) ] ;
2023-02-25 17:44:15 +02:00
2023-03-06 13:29:19 +02:00
if ( value . baseLanguage . empty ( ) )
{
value . baseLanguage = getModLanguage ( modContext ) ;
value . baseValue = localized ;
}
else
{
if ( value . baseValue ! = localized )
logMod - > warn ( " Duplicate registered string '%s' found! Old value: '%s', new value: '%s' " , UID . get ( ) , value . baseValue , localized ) ;
}
2023-02-25 17:44:15 +02:00
}
2023-03-06 13:29:19 +02:00
else
{
StringState result ;
result . baseLanguage = getModLanguage ( modContext ) ;
result . baseValue = localized ;
result . modContext = modContext ;
2023-02-25 17:44:15 +02:00
2023-03-06 13:29:19 +02:00
stringsLocalizations [ UID . get ( ) ] = result ;
}
2022-12-27 22:19:05 +02:00
}
2023-02-09 15:03:49 +02:00
void CGeneralTextHandler : : registerStringOverride ( const std : : string & modContext , const std : : string & language , const TextIdentifier & UID , const std : : string & localized )
2023-01-16 23:20:24 +02:00
{
2023-02-09 15:03:49 +02:00
assert ( ! modContext . empty ( ) ) ;
assert ( ! language . empty ( ) ) ;
2023-08-11 14:59:35 +02:00
// NOTE: implicitly creates entry, intended - strings added by maps, campaigns, vcmi and potentially - UI mods are not registered anywhere at the moment
2023-02-09 15:03:49 +02:00
auto & entry = stringsLocalizations [ UID . get ( ) ] ;
entry . overrideLanguage = language ;
entry . overrideValue = localized ;
if ( entry . modContext . empty ( ) )
entry . modContext = modContext ;
2023-01-16 23:20:24 +02:00
}
2023-02-09 19:15:39 +02:00
bool CGeneralTextHandler : : validateTranslation ( const std : : string & language , const std : : string & modContext , const JsonNode & config ) const
{
bool allPresent = true ;
2023-03-26 22:20:35 +02:00
for ( const auto & string : stringsLocalizations )
2023-02-09 19:15:39 +02:00
{
if ( string . second . modContext ! = modContext )
2023-03-14 19:55:08 +02:00
continue ; // Not our mod
2023-03-14 22:04:08 +02:00
if ( string . second . overrideLanguage = = language )
continue ; // Already translated
2023-02-09 19:15:39 +02:00
if ( string . second . baseLanguage = = language & & ! string . second . baseValue . empty ( ) )
2023-03-14 19:55:08 +02:00
continue ; // Base string already uses our language
2023-02-09 19:15:39 +02:00
2023-08-11 14:59:35 +02:00
if ( string . second . baseLanguage . empty ( ) )
continue ; // String added in localization, not present in base language (e.g. maps/campaigns)
2023-02-09 19:15:39 +02:00
if ( config . Struct ( ) . count ( string . first ) > 0 )
continue ;
if ( allPresent )
logMod - > warn ( " Translation into language '%s' in mod '%s' is incomplete! Missing lines: " , language , modContext ) ;
2023-02-09 21:33:59 +02:00
std : : string currentText ;
if ( string . second . overrideValue . empty ( ) )
currentText = string . second . baseValue ;
else
currentText = string . second . overrideValue ;
2023-02-12 23:52:35 +02:00
logMod - > warn ( R " ( " % s " : " % s " ,) " , string . first , TextOperations : : escapeString ( currentText ) ) ;
2023-02-09 19:15:39 +02:00
allPresent = false ;
}
bool allFound = true ;
2023-04-03 00:12:48 +02:00
// for(const auto & string : config.Struct())
// {
// if (stringsLocalizations.count(string.first) > 0)
// continue;
//
// if (allFound)
// logMod->warn("Translation into language '%s' in mod '%s' has unused lines:", language, modContext);
//
// logMod->warn(R"( "%s" : "%s",)", string.first, TextOperations::escapeString(string.second.String()));
// allFound = false;
// }
2023-02-09 19:15:39 +02:00
return allPresent & & allFound ;
}
void CGeneralTextHandler : : loadTranslationOverrides ( const std : : string & language , const std : : string & modContext , const JsonNode & config )
2023-01-16 23:20:24 +02:00
{
2023-03-13 23:26:44 +02:00
for ( const auto & node : config . Struct ( ) )
2023-02-09 19:15:39 +02:00
registerStringOverride ( modContext , language , node . first , node . second . String ( ) ) ;
2023-01-16 23:20:24 +02:00
}
2022-12-27 22:19:05 +02:00
CGeneralTextHandler : : CGeneralTextHandler ( ) :
victoryConditions ( * this , " core.vcdesc " ) ,
lossCondtions ( * this , " core.lcdesc " ) ,
colors ( * this , " core.plcolors " ) ,
tcommands ( * this , " core.tcommand " ) ,
hcommands ( * this , " core.hallinfo " ) ,
fcommands ( * this , " core.castinfo " ) ,
advobtxt ( * this , " core.advevent " ) ,
restypes ( * this , " core.restypes " ) ,
randsign ( * this , " core.randsign " ) ,
overview ( * this , " core.overview " ) ,
arraytxt ( * this , " core.arraytxt " ) ,
primarySkillNames ( * this , " core.priskill " ) ,
jktexts ( * this , " core.jktext " ) ,
tavernInfo ( * this , " core.tvrninfo " ) ,
tavernRumors ( * this , " core.randtvrn " ) ,
turnDurations ( * this , " core.turndur " ) ,
heroscrn ( * this , " core.heroscrn " ) ,
tentColors ( * this , " core.tentcolr " ) ,
levels ( * this , " core.skilllev " ) ,
zelp ( * this , " core.help " ) ,
2022-12-27 23:17:41 +02:00
allTexts ( * this , " core.genrltxt " ) ,
2022-12-27 22:19:05 +02:00
// pseudo-array, that don't have H3 file with same name
2022-12-27 23:17:41 +02:00
seerEmpty ( * this , " core.seerhut.empty " ) ,
seerNames ( * this , " core.seerhut.names " ) ,
2022-12-27 22:19:05 +02:00
capColors ( * this , " vcmi.capitalColors " ) ,
2022-12-28 00:17:16 +02:00
znpc00 ( * this , " vcmi.znpc00 " ) , // technically - wog
2022-12-27 22:19:05 +02:00
qeModCommands ( * this , " vcmi.quickExchange " )
{
readToVector ( " core.vcdesc " , " DATA/VCDESC.TXT " ) ;
readToVector ( " core.lcdesc " , " DATA/LCDESC.TXT " ) ;
readToVector ( " core.tcommand " , " DATA/TCOMMAND.TXT " ) ;
readToVector ( " core.hallinfo " , " DATA/HALLINFO.TXT " ) ;
readToVector ( " core.castinfo " , " DATA/CASTINFO.TXT " ) ;
readToVector ( " core.advevent " , " DATA/ADVEVENT.TXT " ) ;
readToVector ( " core.restypes " , " DATA/RESTYPES.TXT " ) ;
readToVector ( " core.randsign " , " DATA/RANDSIGN.TXT " ) ;
readToVector ( " core.overview " , " DATA/OVERVIEW.TXT " ) ;
readToVector ( " core.arraytxt " , " DATA/ARRAYTXT.TXT " ) ;
readToVector ( " core.priskill " , " DATA/PRISKILL.TXT " ) ;
readToVector ( " core.jktext " , " DATA/JKTEXT.TXT " ) ;
readToVector ( " core.tvrninfo " , " DATA/TVRNINFO.TXT " ) ;
readToVector ( " core.turndur " , " DATA/TURNDUR.TXT " ) ;
readToVector ( " core.heroscrn " , " DATA/HEROSCRN.TXT " ) ;
readToVector ( " core.tentcolr " , " DATA/TENTCOLR.TXT " ) ;
readToVector ( " core.skilllev " , " DATA/SKILLLEV.TXT " ) ;
readToVector ( " core.cmpmusic " , " DATA/CMPMUSIC.TXT " ) ;
readToVector ( " core.minename " , " DATA/MINENAME.TXT " ) ;
readToVector ( " core.mineevnt " , " DATA/MINEEVNT.TXT " ) ;
2012-08-25 11:44:51 +03:00
2021-11-28 21:48:22 +02:00
static const char * QE_MOD_COMMANDS = " DATA/QECOMMANDS.TXT " ;
2023-09-04 12:03:15 +02:00
if ( CResourceHandler : : get ( ) - > existsResource ( TextPath : : builtin ( QE_MOD_COMMANDS ) ) )
2022-12-27 22:19:05 +02:00
readToVector ( " vcmi.quickExchange " , QE_MOD_COMMANDS ) ;
2022-12-28 01:14:19 +02:00
{
2023-09-01 23:26:14 +02:00
CLegacyConfigParser parser ( TextPath : : builtin ( " DATA/RANDTVRN.TXT " ) ) ;
2022-12-28 01:14:19 +02:00
parser . endLine ( ) ;
size_t index = 0 ;
do
{
std : : string line = parser . readString ( ) ;
2023-01-01 22:50:38 +02:00
if ( ! line . empty ( ) )
2022-12-28 01:14:19 +02:00
{
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " core.randtvrn " , index } , line ) ;
2022-12-28 01:14:19 +02:00
index + = 1 ;
}
}
while ( parser . endLine ( ) ) ;
}
2012-08-25 11:44:51 +03:00
{
2023-09-01 23:26:14 +02:00
CLegacyConfigParser parser ( TextPath : : builtin ( " DATA/GENRLTXT.TXT " ) ) ;
2012-08-25 11:44:51 +03:00
parser . endLine ( ) ;
2022-12-27 23:17:41 +02:00
size_t index = 0 ;
2012-08-25 11:44:51 +03:00
do
2008-12-22 19:48:41 +02:00
{
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " core.genrltxt " , index } , parser . readString ( ) ) ;
2022-12-27 23:17:41 +02:00
index + = 1 ;
2008-12-22 19:48:41 +02:00
}
2012-08-25 11:44:51 +03:00
while ( parser . endLine ( ) ) ;
2008-12-22 19:48:41 +02:00
}
2009-05-22 02:50:45 +03:00
{
2023-09-01 23:26:14 +02:00
CLegacyConfigParser parser ( TextPath : : builtin ( " DATA/HELP.TXT " ) ) ;
2022-12-27 22:19:05 +02:00
size_t index = 0 ;
2012-08-25 11:44:51 +03:00
do
2009-05-22 02:50:45 +03:00
{
2012-08-25 11:44:51 +03:00
std : : string first = parser . readString ( ) ;
std : : string second = parser . readString ( ) ;
2023-02-09 15:03:49 +02:00
registerString ( " core " , " core.help. " + std : : to_string ( index ) + " .hover " , first ) ;
registerString ( " core " , " core.help. " + std : : to_string ( index ) + " .help " , second ) ;
2022-12-27 22:19:05 +02:00
index + = 1 ;
2009-05-22 02:50:45 +03:00
}
2012-08-25 11:44:51 +03:00
while ( parser . endLine ( ) ) ;
2009-05-22 02:50:45 +03:00
}
2009-01-11 00:08:18 +02:00
{
2023-09-01 23:26:14 +02:00
CLegacyConfigParser parser ( TextPath : : builtin ( " DATA/PLCOLORS.TXT " ) ) ;
2022-12-27 22:19:05 +02:00
size_t index = 0 ;
2012-08-25 11:44:51 +03:00
do
{
std : : string color = parser . readString ( ) ;
2009-01-11 00:08:18 +02:00
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " core.plcolors " , index } , color ) ;
2012-08-25 11:44:51 +03:00
color [ 0 ] = toupper ( color [ 0 ] ) ;
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " vcmi.capitalColors " , index } , color ) ;
2022-12-27 22:19:05 +02:00
index + = 1 ;
2012-08-25 11:44:51 +03:00
}
while ( parser . endLine ( ) ) ;
2009-01-11 00:08:18 +02:00
}
{
2023-09-01 23:26:14 +02:00
CLegacyConfigParser parser ( TextPath : : builtin ( " DATA/SEERHUT.TXT " ) ) ;
2009-01-11 00:08:18 +02:00
2012-08-25 11:44:51 +03:00
//skip header
parser . endLine ( ) ;
2009-01-11 00:08:18 +02:00
2022-12-30 19:09:19 +02:00
for ( size_t i = 0 ; i < 6 ; + + i )
2022-12-27 22:19:05 +02:00
{
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " core.seerhut.empty " , i } , parser . readString ( ) ) ;
2022-12-27 22:19:05 +02:00
}
2013-07-23 18:03:01 +03:00
parser . endLine ( ) ;
2010-01-30 22:53:47 +02:00
2022-12-30 19:09:19 +02:00
for ( size_t i = 0 ; i < 9 ; + + i ) //9 types of quests
2010-01-26 21:43:15 +02:00
{
2023-02-09 15:03:49 +02:00
std : : string questName = CQuest : : missionName ( static_cast < CQuest : : Emission > ( 1 + i ) ) ;
2022-12-27 23:17:41 +02:00
2022-12-30 19:09:19 +02:00
for ( size_t j = 0 ; j < 5 ; + + j )
2010-01-26 21:43:15 +02:00
{
2022-12-27 23:17:41 +02:00
std : : string questState = CQuest : : missionState ( j ) ;
2012-08-25 11:44:51 +03:00
parser . readString ( ) ; //front description
2022-12-30 19:09:19 +02:00
for ( size_t k = 0 ; k < 6 ; + + k )
2022-12-27 22:19:05 +02:00
{
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " core.seerhut.quest " , questName , questState , k } , parser . readString ( ) ) ;
2022-12-27 22:19:05 +02:00
}
2012-08-25 11:44:51 +03:00
parser . endLine ( ) ;
2010-01-26 21:43:15 +02:00
}
}
2010-01-30 22:53:47 +02:00
2022-12-30 19:09:19 +02:00
for ( size_t k = 0 ; k < 6 ; + + k ) //Time limit
2012-08-25 11:44:51 +03:00
{
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " core.seerhut.time " , k } , parser . readString ( ) ) ;
2012-08-25 11:44:51 +03:00
}
parser . endLine ( ) ;
parser . endLine ( ) ; // empty line
parser . endLine ( ) ; // header
2022-12-30 19:09:19 +02:00
for ( size_t i = 0 ; i < 48 ; + + i )
2012-08-25 11:44:51 +03:00
{
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " core.seerhut.names " , i } , parser . readString ( ) ) ;
2012-08-25 11:44:51 +03:00
parser . endLine ( ) ;
}
2010-01-30 22:53:47 +02:00
}
2012-07-08 19:36:20 +03:00
{
2023-09-01 23:26:14 +02:00
CLegacyConfigParser parser ( TextPath : : builtin ( " DATA/CAMPTEXT.TXT " ) ) ;
2012-07-08 19:36:20 +03:00
2012-08-25 11:44:51 +03:00
//skip header
parser . endLine ( ) ;
std : : string text ;
2022-12-28 00:17:16 +02:00
size_t campaignsCount = 0 ;
2012-08-25 11:44:51 +03:00
do
2010-02-12 17:04:01 +02:00
{
2012-08-25 11:44:51 +03:00
text = parser . readString ( ) ;
if ( ! text . empty ( ) )
2022-12-28 00:17:16 +02:00
{
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " core.camptext.names " , campaignsCount } , text ) ;
2022-12-28 00:17:16 +02:00
campaignsCount + = 1 ;
}
2010-02-12 17:04:01 +02:00
}
2012-08-25 11:44:51 +03:00
while ( parser . endLine ( ) & & ! text . empty ( ) ) ;
2010-02-12 17:04:01 +02:00
2022-12-28 00:32:44 +02:00
for ( size_t campaign = 0 ; campaign < campaignsCount ; campaign + + )
2010-02-12 17:04:01 +02:00
{
2022-12-28 00:17:16 +02:00
size_t region = 0 ;
2012-08-25 11:44:51 +03:00
do // skip empty space and header
2010-02-12 17:04:01 +02:00
{
2012-08-25 11:44:51 +03:00
text = parser . readString ( ) ;
2010-02-12 17:04:01 +02:00
}
2012-08-25 11:44:51 +03:00
while ( parser . endLine ( ) & & text . empty ( ) ) ;
2011-02-07 16:39:17 +02:00
2012-08-25 11:44:51 +03:00
do
{
text = parser . readString ( ) ;
if ( ! text . empty ( ) )
2022-12-28 00:17:16 +02:00
{
2023-02-09 15:03:49 +02:00
registerString ( " core " , { " core.camptext.regions " , std : : to_string ( campaign ) , region } , text ) ;
2022-12-28 00:17:16 +02:00
region + = 1 ;
}
2012-08-25 11:44:51 +03:00
}
while ( parser . endLine ( ) & & ! text . empty ( ) ) ;
2014-07-04 12:48:09 +03:00
2022-12-28 00:17:16 +02:00
scenariosCountPerCampaign . push_back ( region ) ;
2012-08-25 11:44:51 +03:00
}
2011-03-21 10:14:23 +02:00
}
2023-03-15 21:34:29 +02:00
if ( VLC - > settings ( ) - > getBoolean ( EGameSettings : : MODULE_COMMANDERS ) )
2017-05-26 21:58:33 +02:00
{
2023-09-04 12:03:15 +02:00
if ( CResourceHandler : : get ( ) - > existsResource ( TextPath : : builtin ( " DATA/ZNPC00.TXT " ) ) )
2022-12-28 01:14:19 +02:00
readToVector ( " vcmi.znpc00 " , " DATA/ZNPC00.TXT " ) ;
}
}
2023-03-13 23:26:44 +02:00
int32_t CGeneralTextHandler : : pluralText ( const int32_t textIndex , const int32_t count ) const
2017-07-04 13:24:46 +02:00
{
if ( textIndex = = 0 )
return 0 ;
2023-02-09 15:03:49 +02:00
if ( textIndex < 0 )
2017-07-04 13:24:46 +02:00
return - textIndex ;
2023-02-09 15:03:49 +02:00
if ( count = = 1 )
2017-07-04 13:24:46 +02:00
return textIndex ;
2023-02-09 15:03:49 +02:00
return textIndex + 1 ;
2022-12-28 01:14:19 +02:00
}
void CGeneralTextHandler : : dumpAllTexts ( )
{
logGlobal - > info ( " BEGIN TEXT EXPORT " ) ;
2023-03-13 23:26:44 +02:00
for ( const auto & entry : stringsLocalizations )
2023-02-09 15:03:49 +02:00
{
if ( ! entry . second . overrideValue . empty ( ) )
2023-02-12 23:52:35 +02:00
logGlobal - > info ( R " ( " % s " : " % s " ,) " , entry . first , TextOperations : : escapeString ( entry . second . overrideValue ) ) ;
2023-02-09 15:03:49 +02:00
else
2023-02-12 23:52:35 +02:00
logGlobal - > info ( R " ( " % s " : " % s " ,) " , entry . first , TextOperations : : escapeString ( entry . second . baseValue ) ) ;
2023-02-09 15:03:49 +02:00
}
2023-01-16 23:20:24 +02:00
2022-12-28 01:14:19 +02:00
logGlobal - > info ( " END TEXT EXPORT " ) ;
}
size_t CGeneralTextHandler : : getCampaignLength ( size_t campaignID ) const
2022-12-28 00:17:16 +02:00
{
assert ( campaignID < scenariosCountPerCampaign . size ( ) ) ;
2023-01-01 22:50:38 +02:00
if ( campaignID < scenariosCountPerCampaign . size ( ) )
2022-12-28 00:17:16 +02:00
return scenariosCountPerCampaign [ campaignID ] ;
return 0 ;
}
2023-02-09 15:03:49 +02:00
std : : string CGeneralTextHandler : : getModLanguage ( const std : : string & modContext )
{
if ( modContext = = " core " )
return getInstalledLanguage ( ) ;
return VLC - > modh - > getModLanguage ( modContext ) ;
}
std : : string CGeneralTextHandler : : getPreferredLanguage ( )
2023-01-16 23:20:24 +02:00
{
2023-03-14 15:59:33 +02:00
assert ( ! settings [ " general " ] [ " language " ] . String ( ) . empty ( ) ) ;
2023-01-18 00:14:26 +02:00
return settings [ " general " ] [ " language " ] . String ( ) ;
2023-01-16 23:20:24 +02:00
}
2023-02-09 15:03:49 +02:00
std : : string CGeneralTextHandler : : getInstalledLanguage ( )
{
2023-03-14 15:59:33 +02:00
assert ( ! settings [ " session " ] [ " language " ] . String ( ) . empty ( ) ) ;
2023-02-09 15:03:49 +02:00
return settings [ " session " ] [ " language " ] . String ( ) ;
}
2023-01-16 23:20:24 +02:00
std : : string CGeneralTextHandler : : getInstalledEncoding ( )
{
2023-03-14 15:59:33 +02:00
assert ( ! settings [ " session " ] [ " encoding " ] . String ( ) . empty ( ) ) ;
2023-01-16 23:20:24 +02:00
return settings [ " session " ] [ " encoding " ] . String ( ) ;
}
2023-03-13 23:26:44 +02:00
std : : vector < std : : string > CGeneralTextHandler : : findStringsWithPrefix ( const std : : string & prefix )
2022-12-27 22:19:05 +02:00
{
std : : vector < std : : string > result ;
2023-03-13 23:26:44 +02:00
for ( const auto & entry : stringsLocalizations )
2022-12-27 22:19:05 +02:00
{
2023-01-01 22:50:38 +02:00
if ( boost : : algorithm : : starts_with ( entry . first , prefix ) )
2022-12-27 22:19:05 +02:00
result . push_back ( entry . first ) ;
}
return result ;
}
2023-03-13 23:26:44 +02:00
LegacyTextContainer : : LegacyTextContainer ( CGeneralTextHandler & owner , std : : string basePath ) :
2022-12-27 22:19:05 +02:00
owner ( owner ) ,
2023-03-13 23:26:44 +02:00
basePath ( std : : move ( basePath ) )
2022-12-27 22:19:05 +02:00
{ }
2023-01-01 20:54:05 +02:00
std : : string LegacyTextContainer : : operator [ ] ( size_t index ) const
2022-12-27 22:19:05 +02:00
{
2022-12-30 19:09:19 +02:00
return owner . translate ( basePath , index ) ;
2022-12-27 22:19:05 +02:00
}
2023-03-13 23:26:44 +02:00
LegacyHelpContainer : : LegacyHelpContainer ( CGeneralTextHandler & owner , std : : string basePath ) :
2022-12-27 22:19:05 +02:00
owner ( owner ) ,
2023-03-13 23:26:44 +02:00
basePath ( std : : move ( basePath ) )
2022-12-27 22:19:05 +02:00
{ }
std : : pair < std : : string , std : : string > LegacyHelpContainer : : operator [ ] ( size_t index ) const
{
return {
2022-12-28 23:23:11 +02:00
owner . translate ( basePath + " . " + std : : to_string ( index ) + " .hover " ) ,
2022-12-27 22:19:05 +02:00
owner . translate ( basePath + " . " + std : : to_string ( index ) + " .help " )
} ;
}
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_END