3
0

LocalizedStringManager.cpp 96 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "CrySystem_precompiled.h"
  9. #include "LocalizedStringManager.h"
  10. #if defined(AZ_RESTRICTED_PLATFORM)
  11. #undef AZ_RESTRICTED_SECTION
  12. #define LOCALIZEDSTRINGMANAGER_CPP_SECTION_1 1
  13. #endif
  14. #include <ISystem.h>
  15. #include "System.h" // to access InitLocalization()
  16. #include <CryPath.h>
  17. #include <IConsole.h>
  18. #include <IFont.h>
  19. #include <locale.h>
  20. #include <time.h>
  21. #include <AzCore/std/string/conversions.h>
  22. #include <AzFramework/StringFunc/StringFunc.h>
  23. #include <AzCore/std/string/conversions.h>
  24. #include <AzCore/Math/Crc.h>
  25. #define MAX_CELL_COUNT 32
  26. // CVAR names
  27. #if !defined(_RELEASE)
  28. const char c_sys_localization_debug[] = "sys_localization_debug";
  29. const char c_sys_localization_encode[] = "sys_localization_encode";
  30. #endif // !defined(_RELEASE)
  31. #define LOC_WINDOW "Localization"
  32. const char* c_sys_localization_format = "sys_localization_format";
  33. AZ_CVAR(int32_t, sys_localization_format, 0, nullptr, AZ::ConsoleFunctorFlags::Null,
  34. "Usage: sys_localization_format [0..1]\n"
  35. " 0: O3DE Legacy Localization (Excel 2003)\n"
  36. " 1: AGS XML\n"
  37. "Default is 1 (AGS Xml)");
  38. enum ELocalizedXmlColumns
  39. {
  40. ELOCALIZED_COLUMN_SKIP = 0,
  41. ELOCALIZED_COLUMN_KEY,
  42. ELOCALIZED_COLUMN_AUDIOFILE,
  43. ELOCALIZED_COLUMN_CHARACTER_NAME,
  44. ELOCALIZED_COLUMN_SUBTITLE_TEXT,
  45. ELOCALIZED_COLUMN_ACTOR_LINE,
  46. ELOCALIZED_COLUMN_USE_SUBTITLE,
  47. ELOCALIZED_COLUMN_VOLUME,
  48. ELOCALIZED_COLUMN_SOUNDEVENT,
  49. ELOCALIZED_COLUMN_RADIO_RATIO,
  50. ELOCALIZED_COLUMN_EVENTPARAMETER,
  51. ELOCALIZED_COLUMN_SOUNDMOOD,
  52. ELOCALIZED_COLUMN_IS_DIRECT_RADIO,
  53. // legacy names
  54. ELOCALIZED_COLUMN_LEGACY_PERSON,
  55. ELOCALIZED_COLUMN_LEGACY_CHARACTERNAME,
  56. ELOCALIZED_COLUMN_LEGACY_TRANSLATED_CHARACTERNAME,
  57. ELOCALIZED_COLUMN_LEGACY_ENGLISH_DIALOGUE,
  58. ELOCALIZED_COLUMN_LEGACY_TRANSLATION,
  59. ELOCALIZED_COLUMN_LEGACY_YOUR_TRANSLATION,
  60. ELOCALIZED_COLUMN_LEGACY_ENGLISH_SUBTITLE,
  61. ELOCALIZED_COLUMN_LEGACY_TRANSLATED_SUBTITLE,
  62. ELOCALIZED_COLUMN_LEGACY_ORIGINAL_CHARACTER_NAME,
  63. ELOCALIZED_COLUMN_LEGACY_TRANSLATED_CHARACTER_NAME,
  64. ELOCALIZED_COLUMN_LEGACY_ORIGINAL_TEXT,
  65. ELOCALIZED_COLUMN_LEGACY_TRANSLATED_TEXT,
  66. ELOCALIZED_COLUMN_LEGACY_ORIGINAL_ACTOR_LINE,
  67. ELOCALIZED_COLUMN_LEGACY_TRANSLATED_ACTOR_LINE,
  68. ELOCALIZED_COLUMN_LAST,
  69. };
  70. // The order must match to the order of the ELocalizedXmlColumns
  71. static const char* sLocalizedColumnNames[] =
  72. {
  73. // everyhing read by the file will be convert to lower cases
  74. "skip",
  75. "key",
  76. "audio_filename",
  77. "character name",
  78. "subtitle text",
  79. "actor line",
  80. "use subtitle",
  81. "volume",
  82. "prototype event",
  83. "radio ratio",
  84. "eventparameter",
  85. "soundmood",
  86. "is direct radio",
  87. // legacy names
  88. "person",
  89. "character name",
  90. "translated character name",
  91. "english dialogue",
  92. "translation",
  93. "your translation",
  94. "english subtitle",
  95. "translated subtitle",
  96. "original character name",
  97. "translated character name",
  98. "original text",
  99. "translated text",
  100. "original actor line",
  101. "translated actor line",
  102. };
  103. //Please ensure that this array matches the contents of EPlatformIndependentLanguageID in ILocalizationManager.h
  104. static const char* PLATFORM_INDEPENDENT_LANGUAGE_NAMES[ ILocalizationManager::ePILID_MAX_OR_INVALID ] =
  105. {
  106. "en-US", // English (USA)
  107. "en-GB", // English (UK)
  108. "de-DE", // German
  109. "ru-RU", // Russian (Russia)
  110. "pl-PL", // Polish
  111. "tr-TR", // Turkish
  112. "es-ES", // Spanish (Spain)
  113. "es-MX", // Spanish (Mexico)
  114. "fr-FR", // French (France)
  115. "fr-CA", // French (Canada)
  116. "it-IT", // Italian
  117. "pt-PT", // Portugese (Portugal)
  118. "pt-BR", // Portugese (Brazil)
  119. "ja-JP", // Japanese
  120. "ko-KR", // Korean
  121. "zh-CHT", // Traditional Chinese
  122. "zh-CHS", // Simplified Chinese
  123. "nl-NL", // Dutch (The Netherlands)
  124. "fi-FI", // Finnish
  125. "sv-SE", // Swedish
  126. "cs-CZ", // Czech
  127. "no-NO", // Norwegian
  128. "ar-SA", // Arabic (Saudi Arabia)
  129. "da-DK" // Danish (Denmark)
  130. };
  131. #if defined(WIN32) || defined(WIN64)
  132. namespace
  133. {
  134. #if defined(WIN32)
  135. time_t gmt_to_local_win32(void)
  136. {
  137. TIME_ZONE_INFORMATION tzinfo;
  138. DWORD dwStandardDaylight;
  139. long bias;
  140. dwStandardDaylight = GetTimeZoneInformation(&tzinfo);
  141. bias = tzinfo.Bias;
  142. if (dwStandardDaylight == TIME_ZONE_ID_STANDARD)
  143. {
  144. bias += tzinfo.StandardBias;
  145. }
  146. if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT)
  147. {
  148. bias += tzinfo.DaylightBias;
  149. }
  150. return (-bias * 60);
  151. }
  152. #endif // #if defined(WIN32)
  153. time_t DateToSecondsUTC(struct tm& inDate)
  154. {
  155. #if defined(WIN32)
  156. return mktime(&inDate) + gmt_to_local_win32();
  157. #else
  158. return mktime(&inDate);
  159. #endif // #if defined(WIN32)
  160. }
  161. }
  162. #endif // #if defined(WIN32) || defined(WIN64)
  163. //////////////////////////////////////////////////////////////////////////
  164. #if !defined(_RELEASE)
  165. static void ReloadDialogData([[maybe_unused]] IConsoleCmdArgs* pArgs)
  166. {
  167. LocalizationManagerRequestBus::Broadcast(&LocalizationManagerRequestBus::Events::ReloadData);
  168. //CSystem *pSystem = (CSystem*) gEnv->pSystem;
  169. //pSystem->InitLocalization();
  170. //pSystem->OpenBasicPaks();
  171. }
  172. #endif //#if !defined(_RELEASE)
  173. //////////////////////////////////////////////////////////////////////////
  174. #if !defined(_RELEASE)
  175. static void TestFormatMessage ([[maybe_unused]] IConsoleCmdArgs* pArgs)
  176. {
  177. AZStd::string fmt1 ("abc %1 def % gh%2i %");
  178. AZStd::string fmt2 ("abc %[action:abc] %2 def % gh%1i %1");
  179. AZStd::string out1, out2;
  180. LocalizationManagerRequestBus::Broadcast(&LocalizationManagerRequestBus::Events::FormatStringMessage, out1, fmt1, "first", "second", "third", nullptr);
  181. CryLogAlways("%s", out1.c_str());
  182. LocalizationManagerRequestBus::Broadcast(&LocalizationManagerRequestBus::Events::FormatStringMessage, out2, fmt2, "second", nullptr, nullptr, nullptr);
  183. CryLogAlways("%s", out2.c_str());
  184. }
  185. #endif //#if !defined(_RELEASE)
  186. //////////////////////////////////////////////////////////////////////
  187. // Construction/Destruction
  188. //////////////////////////////////////////////////////////////////////
  189. //////////////////////////////////////////////////////////////////////
  190. CLocalizedStringsManager::CLocalizedStringsManager(ISystem* pSystem)
  191. : m_cvarLocalizationDebug(0)
  192. , m_cvarLocalizationEncode(1)
  193. , m_availableLocalizations(0)
  194. {
  195. m_pSystem = pSystem;
  196. m_pSystem->GetISystemEventDispatcher()->RegisterListener(this);
  197. m_languages.reserve(4);
  198. m_pLanguage = 0;
  199. #if !defined(_RELEASE)
  200. m_haveWarnedAboutAtLeastOneLabel = false;
  201. REGISTER_COMMAND("ReloadDialogData", ReloadDialogData, VF_NULL,
  202. "Reloads all localization dependent XML sheets for the currently set language.");
  203. REGISTER_COMMAND("_TestFormatMessage", TestFormatMessage, VF_NULL, "");
  204. REGISTER_CVAR2(c_sys_localization_debug, &m_cvarLocalizationDebug, m_cvarLocalizationDebug, VF_CHEAT,
  205. "Toggles debugging of the Localization Manager.\n"
  206. "Usage: sys_localization_debug [0..3]\n"
  207. "1: outputs warnings\n"
  208. "2: outputs extended information and warnings\n"
  209. "3: outputs CRC32 hashes and strings to help detect clashes\n"
  210. "Default is 0 (off).");
  211. REGISTER_CVAR2(c_sys_localization_encode, &m_cvarLocalizationEncode, m_cvarLocalizationEncode, VF_REQUIRE_APP_RESTART,
  212. "Toggles encoding of translated text to save memory. REQUIRES RESTART.\n"
  213. "Usage: sys_localization_encode [0..1]\n"
  214. "0: No encoding, store as wide strings\n"
  215. "1: Huffman encode translated text, saves approx 30% with a small runtime performance cost\n"
  216. "Default is 1.");
  217. #endif //#if !defined(_RELEASE)
  218. //Check that someone hasn't added a language ID without a language name
  219. assert(PLATFORM_INDEPENDENT_LANGUAGE_NAMES[ ILocalizationManager::ePILID_MAX_OR_INVALID - 1 ] != 0);
  220. // Populate available languages by scanning the localization directory for paks
  221. // Default to US English if language is not supported
  222. AZStd::string sPath;
  223. const AZStd::string sLocalizationFolder(PathUtil::GetLocalizationFolder());
  224. ILocalizationManager::TLocalizationBitfield availableLanguages = 0;
  225. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  226. // test language name against supported languages
  227. for (int i = 0; i < ILocalizationManager::ePILID_MAX_OR_INVALID; i++)
  228. {
  229. AZStd::string sCurrentLanguage = LangNameFromPILID((ILocalizationManager::EPlatformIndependentLanguageID)i);
  230. sPath = sLocalizationFolder.c_str() + sCurrentLanguage;
  231. AZStd::to_lower(sPath.begin(), sPath.end());
  232. if (fileIO && fileIO->IsDirectory(sPath.c_str()))
  233. {
  234. availableLanguages |= ILocalizationManager::LocalizationBitfieldFromPILID((ILocalizationManager::EPlatformIndependentLanguageID)i);
  235. if (m_cvarLocalizationDebug >= 2)
  236. {
  237. AZ_TracePrintf("Localization", "Detected language support for %s (id %d)", sCurrentLanguage.c_str(), i);
  238. }
  239. }
  240. }
  241. int32_t localizationFormat{};
  242. if (auto console = AZ::Interface<AZ::IConsole>::Get();
  243. console !=nullptr)
  244. {
  245. console->GetCvarValue(c_sys_localization_format, localizationFormat);
  246. }
  247. AZ_Warning("Localization", !(localizationFormat == 0 && availableLanguages == 0 && ProjectUsesLocalization()), "No localization files found!");
  248. SetAvailableLocalizationsBitfield(availableLanguages);
  249. LocalizationManagerRequestBus::Handler::BusConnect();
  250. }
  251. //////////////////////////////////////////////////////////////////////
  252. CLocalizedStringsManager::~CLocalizedStringsManager()
  253. {
  254. FreeData();
  255. LocalizationManagerRequestBus::Handler::BusDisconnect();
  256. }
  257. //////////////////////////////////////////////////////////////////////
  258. void CLocalizedStringsManager::GetLoadedTags(TLocalizationTagVec& tagVec)
  259. {
  260. TTagFileNames::const_iterator end = m_tagFileNames.end();
  261. for (TTagFileNames::const_iterator it = m_tagFileNames.begin(); it != end; ++it)
  262. {
  263. if (it->second.loaded)
  264. {
  265. tagVec.push_back(it->first);
  266. }
  267. }
  268. }
  269. //////////////////////////////////////////////////////////////////////
  270. void CLocalizedStringsManager::FreeLocalizationData()
  271. {
  272. AutoLock lock(m_cs); //Make sure to lock, as this is a modifying operation
  273. ListAndClearProblemLabels();
  274. for (uint32 i = 0; i < m_languages.size(); i++)
  275. {
  276. if (m_cvarLocalizationEncode == 1)
  277. {
  278. auto pLanguage = m_languages[i];
  279. for (uint8 iEncoder = 0; iEncoder < pLanguage->m_vEncoders.size(); iEncoder++)
  280. {
  281. SAFE_DELETE(pLanguage->m_vEncoders[iEncoder]);
  282. }
  283. }
  284. std::for_each(m_languages[i]->m_vLocalizedStrings.begin(), m_languages[i]->m_vLocalizedStrings.end(), stl::container_object_deleter());
  285. m_languages[i]->m_keysMap.clear();
  286. m_languages[i]->m_vLocalizedStrings.clear();
  287. }
  288. m_loadedTables.clear();
  289. }
  290. //////////////////////////////////////////////////////////////////////
  291. void CLocalizedStringsManager::FreeData()
  292. {
  293. FreeLocalizationData();
  294. for (uint32 i = 0; i < m_languages.size(); i++)
  295. {
  296. delete m_languages[i];
  297. }
  298. m_languages.resize(0);
  299. m_loadedTables.clear();
  300. m_pLanguage = 0;
  301. }
  302. //////////////////////////////////////////////////////////////////////////
  303. const char* CLocalizedStringsManager::LangNameFromPILID(const ILocalizationManager::EPlatformIndependentLanguageID id)
  304. {
  305. assert(id >= 0 && id < ILocalizationManager::ePILID_MAX_OR_INVALID);
  306. return PLATFORM_INDEPENDENT_LANGUAGE_NAMES[ id ];
  307. }
  308. //////////////////////////////////////////////////////////////////////////
  309. ILocalizationManager::EPlatformIndependentLanguageID CLocalizedStringsManager::PILIDFromLangName(AZStd::string langName)
  310. {
  311. for (int i = 0; i < ILocalizationManager::ePILID_MAX_OR_INVALID; i++)
  312. {
  313. if (!_stricmp(langName.c_str(), PLATFORM_INDEPENDENT_LANGUAGE_NAMES[i]))
  314. {
  315. return (ILocalizationManager::EPlatformIndependentLanguageID)i;
  316. }
  317. }
  318. return ILocalizationManager::ePILID_MAX_OR_INVALID;
  319. }
  320. #if defined(AZ_RESTRICTED_PLATFORM)
  321. #define AZ_RESTRICTED_SECTION LOCALIZEDSTRINGMANAGER_CPP_SECTION_1
  322. #include AZ_RESTRICTED_FILE(LocalizedStringManager_cpp)
  323. #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
  324. #undef AZ_RESTRICTED_SECTION_IMPLEMENTED
  325. #endif // AZ_RESTRICTED_SECTION_IMPLEMENTED
  326. #else
  327. //////////////////////////////////////////////////////////////////////////
  328. ILocalizationManager::EPlatformIndependentLanguageID CLocalizedStringsManager::GetSystemLanguage()
  329. {
  330. return ILocalizationManager::EPlatformIndependentLanguageID::ePILID_English_US;
  331. }
  332. #endif // defined(AZ_RESTRICTED_PLATFORM)
  333. //Uses bitwise operations to compare the localizations we provide in this SKU and the languages that the platform supports.
  334. //Returns !0 if we provide more localizations than are available as system languages
  335. ILocalizationManager::TLocalizationBitfield CLocalizedStringsManager::MaskSystemLanguagesFromSupportedLocalizations(const ILocalizationManager::TLocalizationBitfield systemLanguages)
  336. {
  337. return (~systemLanguages) & m_availableLocalizations;
  338. }
  339. //Returns !0 if the language is supported.
  340. ILocalizationManager::TLocalizationBitfield CLocalizedStringsManager::IsLanguageSupported(const ILocalizationManager::EPlatformIndependentLanguageID id)
  341. {
  342. return m_availableLocalizations & (1 << id);
  343. }
  344. //////////////////////////////////////////////////////////////////////////
  345. void CLocalizedStringsManager::SetAvailableLocalizationsBitfield(const ILocalizationManager::TLocalizationBitfield availableLocalizations)
  346. {
  347. m_availableLocalizations = availableLocalizations;
  348. }
  349. //////////////////////////////////////////////////////////////////////
  350. const char* CLocalizedStringsManager::GetLanguage()
  351. {
  352. if (m_pLanguage == 0)
  353. {
  354. return "";
  355. }
  356. return m_pLanguage->sLanguage.c_str();
  357. }
  358. //////////////////////////////////////////////////////////////////////
  359. bool CLocalizedStringsManager::SetLanguage(const char* sLanguage)
  360. {
  361. if (m_cvarLocalizationDebug >= 2)
  362. {
  363. CryLog("<Localization> Set language to %s", sLanguage);
  364. }
  365. // Check if already language loaded.
  366. for (uint32 i = 0; i < m_languages.size(); i++)
  367. {
  368. if (_stricmp(sLanguage, m_languages[i]->sLanguage.c_str()) == 0)
  369. {
  370. InternalSetCurrentLanguage(m_languages[i]);
  371. return true;
  372. }
  373. }
  374. SLanguage* pLanguage = new SLanguage;
  375. m_languages.push_back(pLanguage);
  376. if (m_cvarLocalizationDebug >= 2)
  377. {
  378. CryLog("<Localization> Insert new language to %s", sLanguage);
  379. }
  380. pLanguage->sLanguage = sLanguage;
  381. InternalSetCurrentLanguage(pLanguage);
  382. //-------------------------------------------------------------------------------------------------
  383. // input localization
  384. //-------------------------------------------------------------------------------------------------
  385. // keyboard
  386. for (int i = 0; i <= 0x80; i++)
  387. {
  388. AddControl(i);
  389. }
  390. // mouse
  391. for (int i = 1; i <= 0x0f; i++)
  392. {
  393. AddControl(i * 0x10000);
  394. }
  395. return (true);
  396. }
  397. //////////////////////////////////////////////////////////////////////////
  398. int CLocalizedStringsManager::GetLocalizationFormat() const
  399. {
  400. int32_t localizationFormat{};
  401. if (auto console = AZ::Interface<AZ::IConsole>::Get();
  402. console !=nullptr)
  403. {
  404. console->GetCvarValue(c_sys_localization_format, localizationFormat);
  405. }
  406. return localizationFormat;
  407. }
  408. //////////////////////////////////////////////////////////////////////////
  409. AZStd::string CLocalizedStringsManager::GetLocalizedSubtitleFilePath(const AZStd::string& localVideoPath, const AZStd::string& subtitleFileExtension) const
  410. {
  411. AZStd::string sLocalizationFolder(PathUtil::GetLocalizationFolder().c_str());
  412. size_t backSlashIdx = sLocalizationFolder.find_first_of("\\", 0);
  413. if (backSlashIdx != AZStd::string::npos)
  414. {
  415. sLocalizationFolder.replace(backSlashIdx, 2, "/");
  416. }
  417. AZStd::string filePath(m_pLanguage->sLanguage.c_str());
  418. filePath = sLocalizationFolder.c_str() + filePath + "/" + localVideoPath;
  419. return filePath.substr(0, filePath.find_last_of('.')).append(subtitleFileExtension);
  420. }
  421. //////////////////////////////////////////////////////////////////////////
  422. AZStd::string CLocalizedStringsManager::GetLocalizedLocXMLFilePath(const AZStd::string & localXmlPath) const
  423. {
  424. AZStd::string sLocalizationFolder(PathUtil::GetLocalizationFolder().c_str());
  425. size_t backSlashIdx = sLocalizationFolder.find_first_of("\\", 0);
  426. if (backSlashIdx != AZStd::string::npos)
  427. {
  428. sLocalizationFolder.replace(backSlashIdx, 2, "/");
  429. }
  430. const AZStd::string& filePath = AZStd::string::format("%s%s/%s", sLocalizationFolder.c_str(), m_pLanguage->sLanguage.c_str(), localXmlPath.c_str());
  431. return filePath.substr(0, filePath.find_last_of('.')).append(".loc.xml");
  432. }
  433. //////////////////////////////////////////////////////////////////////////
  434. void CLocalizedStringsManager::AddControl([[maybe_unused]] int nKey)
  435. {
  436. }
  437. //////////////////////////////////////////////////////////////////////////
  438. void CLocalizedStringsManager::ParseFirstLine(IXmlTableReader* pXmlTableReader, char* nCellIndexToType, std::map<int, AZStd::string>& SoundMoodIndex, std::map<int, AZStd::string>& EventParameterIndex)
  439. {
  440. AZStd::string sCellContent;
  441. for (;; )
  442. {
  443. int nCellIndex = 0;
  444. const char* pContent = 0;
  445. size_t contentSize = 0;
  446. if (!pXmlTableReader->ReadCell(nCellIndex, pContent, contentSize))
  447. {
  448. break;
  449. }
  450. if (nCellIndex >= MAX_CELL_COUNT)
  451. {
  452. break;
  453. }
  454. if (contentSize <= 0)
  455. {
  456. continue;
  457. }
  458. sCellContent.assign(pContent, contentSize);
  459. AZStd::to_lower(sCellContent.begin(), sCellContent.end());
  460. for (int i = 0; i < sizeof(sLocalizedColumnNames) / sizeof(sLocalizedColumnNames[0]); ++i)
  461. {
  462. const char* pFind = strstr(sCellContent.c_str(), sLocalizedColumnNames[i]);
  463. if (pFind != 0)
  464. {
  465. nCellIndexToType[nCellIndex] = static_cast<char>(i);
  466. // find SoundMood
  467. if (i == ELOCALIZED_COLUMN_SOUNDMOOD)
  468. {
  469. const char* pSoundMoodName = pFind + strlen(sLocalizedColumnNames[i]) + 1;
  470. int nSoundMoodNameLength = static_cast<int>(sCellContent.length() - strlen(sLocalizedColumnNames[i]) - 1);
  471. if (nSoundMoodNameLength > 0)
  472. {
  473. SoundMoodIndex[nCellIndex] = pSoundMoodName;
  474. }
  475. }
  476. // find EventParameter
  477. if (i == ELOCALIZED_COLUMN_EVENTPARAMETER)
  478. {
  479. const char* pParameterName = pFind + strlen(sLocalizedColumnNames[i]) + 1;
  480. int nParameterNameLength = static_cast<int>(sCellContent.length() - strlen(sLocalizedColumnNames[i]) - 1);
  481. if (nParameterNameLength > 0)
  482. {
  483. EventParameterIndex[nCellIndex] = pParameterName;
  484. }
  485. }
  486. break;
  487. }
  488. // HACK until all columns are renamed to "Translation"
  489. //if (_stricmp(sCellContent, "Your Translation") == 0)
  490. //{
  491. // nCellIndexToType[nCellIndex] = ELOCALIZED_COLUMN_TRANSLATED_ACTOR_LINE;
  492. // break;
  493. //}
  494. }
  495. }
  496. }
  497. // copy characters to lower-case 0-terminated buffer
  498. static void CopyLowercase(char* dst, size_t dstSize, const char* src, size_t srcCount)
  499. {
  500. if (dstSize > 0)
  501. {
  502. if (srcCount > dstSize - 1)
  503. {
  504. srcCount = dstSize - 1;
  505. }
  506. while (srcCount--)
  507. {
  508. const char c = *src++;
  509. *dst++ = (c <= 'Z' && c >= 'A') ? c + ('a' - 'A') : c;
  510. }
  511. *dst = '\0';
  512. }
  513. }
  514. //////////////////////////////////////////////////////////////////////////
  515. static void ReplaceEndOfLine(AZStd::fixed_string<CLocalizedStringsManager::LOADING_FIXED_STRING_LENGTH>& s)
  516. {
  517. const AZStd::string oldSubstr("\\n");
  518. const AZStd::string newSubstr(" \n");
  519. size_t pos = 0;
  520. for (;; )
  521. {
  522. pos = s.find(oldSubstr, pos);
  523. if (pos == AZStd::fixed_string<CLocalizedStringsManager::LOADING_FIXED_STRING_LENGTH>::npos)
  524. {
  525. return;
  526. }
  527. s.replace(pos, oldSubstr.length(), newSubstr);
  528. }
  529. }
  530. //////////////////////////////////////////////////////////////////////////
  531. void CLocalizedStringsManager::OnSystemEvent(
  532. ESystemEvent eEvent, [[maybe_unused]] UINT_PTR wparam, [[maybe_unused]] UINT_PTR lparam)
  533. {
  534. // might want to add an event which tells us that we are loading the main menu
  535. // so everything can be unloaded and init files reloaded so safe some memory
  536. switch (eEvent)
  537. {
  538. case ESYSTEM_EVENT_LEVEL_LOAD_START:
  539. {
  540. // This event is here not of interest while we're in the Editor.
  541. if (!gEnv->IsEditor())
  542. {
  543. if (m_cvarLocalizationDebug >= 2)
  544. {
  545. CryLog("<Localization> Loading Requested Tags");
  546. }
  547. for (TStringVec::iterator it = m_tagLoadRequests.begin(); it != m_tagLoadRequests.end(); ++it)
  548. {
  549. LoadLocalizationDataByTag(it->c_str());
  550. }
  551. }
  552. m_tagLoadRequests.clear();
  553. break;
  554. }
  555. case ESYSTEM_EVENT_EDITOR_ON_INIT:
  556. {
  557. // Load all tags after the Editor has finished initialization.
  558. for (TTagFileNames::iterator it = m_tagFileNames.begin(); it != m_tagFileNames.end(); ++it)
  559. {
  560. LoadLocalizationDataByTag(it->first.c_str());
  561. }
  562. break;
  563. }
  564. }
  565. }
  566. //////////////////////////////////////////////////////////////////////////
  567. bool CLocalizedStringsManager::InitLocalizationData(
  568. const char* sFileName, [[maybe_unused]] bool bReload)
  569. {
  570. XmlNodeRef root = m_pSystem->LoadXmlFromFile(sFileName);
  571. if (!root)
  572. {
  573. CryLog("Loading Localization File %s failed!", sFileName);
  574. return false;
  575. }
  576. for (int i = 0; i < root->getChildCount(); i++)
  577. {
  578. XmlNodeRef typeNode = root->getChild(i);
  579. AZStd::string sType = typeNode->getTag();
  580. // tags should be unique
  581. if (m_tagFileNames.find(sType) != m_tagFileNames.end())
  582. {
  583. continue;
  584. }
  585. TStringVec vEntries;
  586. for (int j = 0; j < typeNode->getChildCount(); j++)
  587. {
  588. XmlNodeRef entry = typeNode->getChild(j);
  589. if (!entry->isTag("entry"))
  590. {
  591. continue;
  592. }
  593. vEntries.push_back(entry->getContent());
  594. }
  595. CRY_ASSERT(m_tagFileNames.size() < 255);
  596. uint8 curNumTags = static_cast<uint8>(m_tagFileNames.size());
  597. m_tagFileNames[sType].filenames = vEntries;
  598. m_tagFileNames[sType].id = curNumTags + 1;
  599. m_tagFileNames[sType].loaded = false;
  600. }
  601. return true;
  602. }
  603. //////////////////////////////////////////////////////////////////////////
  604. bool CLocalizedStringsManager::RequestLoadLocalizationDataByTag(const char* sTag)
  605. {
  606. TTagFileNames::iterator it = m_tagFileNames.find(sTag);
  607. if (it == m_tagFileNames.end())
  608. {
  609. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "[LocError] RequestLoadLocalizationDataByTag - Localization tag '%s' not found", sTag);
  610. return false;
  611. }
  612. if (m_cvarLocalizationDebug >= 2)
  613. {
  614. CryLog("<Localization> RequestLoadLocalizationDataByTag %s", sTag);
  615. }
  616. m_tagLoadRequests.push_back(sTag);
  617. return true;
  618. }
  619. //////////////////////////////////////////////////////////////////////////
  620. bool CLocalizedStringsManager::LoadLocalizationDataByTag(
  621. const char* sTag, bool bReload)
  622. {
  623. TTagFileNames::iterator it = m_tagFileNames.find(sTag);
  624. if (it == m_tagFileNames.end())
  625. {
  626. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "[LocError] LoadLocalizationDataByTag - Localization tag '%s' not found", sTag);
  627. return false;
  628. }
  629. if (it->second.loaded)
  630. {
  631. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "[LocError] LoadLocalizationDataByTag - Already loaded tag '%s'", sTag);
  632. return true;
  633. }
  634. bool bResult = true;
  635. stack_string const sLocalizationFolder(PathUtil::GetLocalizationFolder());
  636. int32_t localizationFormat{};
  637. if (auto console = AZ::Interface<AZ::IConsole>::Get();
  638. console !=nullptr)
  639. {
  640. console->GetCvarValue(c_sys_localization_format, localizationFormat);
  641. }
  642. LoadFunc loadFunction = GetLoadFunction();
  643. TStringVec& vEntries = it->second.filenames;
  644. for (TStringVec::iterator it2 = vEntries.begin(); it2 != vEntries.end(); ++it2)
  645. {
  646. //Only load files of the correct type for the configured format
  647. if ((localizationFormat == 0 && strstr(it2->c_str(), ".xml")) || (localizationFormat == 1 && strstr(it2->c_str(), ".agsxml")))
  648. {
  649. bResult &= (this->*loadFunction)(it2->c_str(), it->second.id, bReload);
  650. }
  651. }
  652. if (m_cvarLocalizationDebug >= 2)
  653. {
  654. CryLog("<Localization> LoadLocalizationDataByTag %s with result %d", sTag, bResult);
  655. }
  656. it->second.loaded = true;
  657. return bResult;
  658. }
  659. //////////////////////////////////////////////////////////////////////////
  660. bool CLocalizedStringsManager::ReleaseLocalizationDataByTag(
  661. const char* sTag)
  662. {
  663. INDENT_LOG_DURING_SCOPE(true, "Releasing localization data with the tag '%s'", sTag);
  664. ListAndClearProblemLabels();
  665. TTagFileNames::iterator it = m_tagFileNames.find(sTag);
  666. if (it == m_tagFileNames.end())
  667. {
  668. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "[LocError] ReleaseLocalizationDataByTag - Localization tag '%s' not found", sTag);
  669. return false;
  670. }
  671. if (it->second.loaded == false)
  672. {
  673. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "[LocError] ReleaseLocalizationDataByTag - tag '%s' not loaded", sTag);
  674. return false;
  675. }
  676. const uint8 nTagID = it->second.id;
  677. tmapFilenames newLoadedTables;
  678. for (tmapFilenames::iterator iter = m_loadedTables.begin(); iter != m_loadedTables.end(); iter++)
  679. {
  680. if (iter->second.nTagID != nTagID)
  681. {
  682. newLoadedTables[iter->first] = iter->second;
  683. }
  684. }
  685. m_loadedTables = newLoadedTables;
  686. if (m_pLanguage)
  687. {
  688. //LARGE_INTEGER liStart;
  689. //QueryPerformanceCounter(&liStart);
  690. AutoLock lock(m_cs); //Make sure to lock, as this is a modifying operation
  691. bool bMapEntryErased = false;
  692. //First, remove entries from the map
  693. for (StringsKeyMap::iterator keyMapIt = m_pLanguage->m_keysMap.begin(); keyMapIt != m_pLanguage->m_keysMap.end(); )
  694. {
  695. if (keyMapIt->second->nTagID == nTagID)
  696. {
  697. //VECTORMAP ONLY
  698. keyMapIt = m_pLanguage->m_keysMap.erase(keyMapIt);
  699. bMapEntryErased = true;
  700. }
  701. else
  702. {
  703. keyMapIt++;
  704. }
  705. }
  706. if (bMapEntryErased == true)
  707. {
  708. StringsKeyMap newMap = m_pLanguage->m_keysMap;
  709. m_pLanguage->m_keysMap.clearAndFreeMemory();
  710. m_pLanguage->m_keysMap = newMap;
  711. }
  712. bool bVecEntryErased = false;
  713. //Then remove the entries in the storage vector
  714. const int32 numEntries = static_cast<int32>(m_pLanguage->m_vLocalizedStrings.size());
  715. for (int32 i = numEntries - 1; i >= 0; i--)
  716. {
  717. SLocalizedStringEntry* entry = m_pLanguage->m_vLocalizedStrings[i];
  718. PREFAST_ASSUME(entry);
  719. if (entry->nTagID == nTagID)
  720. {
  721. if (m_cvarLocalizationEncode == 1)
  722. {
  723. if (entry->huffmanTreeIndex != -1)
  724. {
  725. HuffmanCoder* pCoder = m_pLanguage->m_vEncoders[entry->huffmanTreeIndex];
  726. if (pCoder != NULL)
  727. {
  728. pCoder->DecRef();
  729. if (pCoder->RefCount() == 0)
  730. {
  731. if (m_cvarLocalizationDebug >= 2)
  732. {
  733. CryLog("<Localization> Releasing coder %u as it no longer has associated strings", entry->huffmanTreeIndex);
  734. }
  735. //This coding table no longer needed, it has no more associated strings
  736. SAFE_DELETE(m_pLanguage->m_vEncoders[entry->huffmanTreeIndex]);
  737. }
  738. }
  739. }
  740. }
  741. bVecEntryErased = true;
  742. delete(entry);
  743. m_pLanguage->m_vLocalizedStrings.erase(m_pLanguage->m_vLocalizedStrings.begin() + i);
  744. }
  745. }
  746. //Shrink the vector if necessary
  747. if (bVecEntryErased == true)
  748. {
  749. SLanguage::TLocalizedStringEntries newVec = m_pLanguage->m_vLocalizedStrings;
  750. m_pLanguage->m_vLocalizedStrings.clear();
  751. m_pLanguage->m_vLocalizedStrings = newVec;
  752. }
  753. }
  754. if (m_cvarLocalizationDebug >= 2)
  755. {
  756. CryLog("<Localization> ReleaseLocalizationDataByTag %s", sTag);
  757. }
  758. it->second.loaded = false;
  759. return true;
  760. }
  761. //////////////////////////////////////////////////////////////////////////
  762. bool CLocalizedStringsManager::LoadAllLocalizationData(bool bReload)
  763. {
  764. for (TTagFileNames::iterator it = m_tagFileNames.begin(); it != m_tagFileNames.end(); ++it)
  765. {
  766. if(!LoadLocalizationDataByTag(it->first.c_str(), bReload))
  767. return false;
  768. }
  769. return true;
  770. }
  771. //////////////////////////////////////////////////////////////////////////
  772. bool CLocalizedStringsManager::LoadExcelXmlSpreadsheet(const char* sFileName, bool bReload)
  773. {
  774. LoadFunc loadFunction = GetLoadFunction();
  775. return (this->*loadFunction)(sFileName, 0, bReload);
  776. }
  777. enum class YesNoType
  778. {
  779. Yes,
  780. No,
  781. Invalid
  782. };
  783. // parse the yes/no string
  784. /*!
  785. \param szString any of the following strings: yes, enable, true, 1, no, disable, false, 0
  786. \return YesNoType::Yes if szString is yes/enable/true/1, YesNoType::No if szString is no, disable, false, 0 and YesNoType::Invalid if the string is not one of the expected values.
  787. */
  788. inline YesNoType ToYesNoType(const char* szString)
  789. {
  790. if (!_stricmp(szString, "yes")
  791. || !_stricmp(szString, "enable")
  792. || !_stricmp(szString, "true")
  793. || !_stricmp(szString, "1"))
  794. {
  795. return YesNoType::Yes;
  796. }
  797. if (!_stricmp(szString, "no")
  798. || !_stricmp(szString, "disable")
  799. || !_stricmp(szString, "false")
  800. || !_stricmp(szString, "0"))
  801. {
  802. return YesNoType::No;
  803. }
  804. return YesNoType::Invalid;
  805. }
  806. //////////////////////////////////////////////////////////////////////
  807. // Loads a string-table from a Excel XML Spreadsheet file.
  808. bool CLocalizedStringsManager::DoLoadExcelXmlSpreadsheet(const char* sFileName, uint8 nTagID, bool bReload)
  809. {
  810. if (!m_pLanguage)
  811. {
  812. return false;
  813. }
  814. //check if this table has already been loaded
  815. if (!bReload)
  816. {
  817. if (m_loadedTables.find(AZStd::string(sFileName)) != m_loadedTables.end())
  818. {
  819. return (true);
  820. }
  821. }
  822. ListAndClearProblemLabels();
  823. IXmlTableReader* const pXmlTableReader = m_pSystem->GetXmlUtils()->CreateXmlTableReader();
  824. if (!pXmlTableReader)
  825. {
  826. CryLog("Loading Localization File %s failed (XML system failure)!", sFileName);
  827. return false;
  828. }
  829. XmlNodeRef root;
  830. AZStd::string sPath;
  831. {
  832. const AZStd::string sLocalizationFolder(PathUtil::GetLocalizationRoot());
  833. const AZStd::string& languageFolder = m_pLanguage->sLanguage;
  834. sPath = sLocalizationFolder.c_str() + languageFolder + PathUtil::GetSlash() + sFileName;
  835. root = m_pSystem->LoadXmlFromFile(sPath.c_str());
  836. if (!root)
  837. {
  838. CryLog("Loading Localization File %s failed!", sPath.c_str());
  839. pXmlTableReader->Release();
  840. return false;
  841. }
  842. }
  843. // bug search, re-export to a file to compare it
  844. //string sReExport = sFileName;
  845. //sReExport += ".re";
  846. //root->saveToFile(sReExport.c_str());
  847. CryLog("Loading Localization File %s", sFileName);
  848. INDENT_LOG_DURING_SCOPE();
  849. //Create a huffman coding table for these strings - if they're going to be encoded or compressed
  850. HuffmanCoder* pEncoder = NULL;
  851. uint8 iEncoder = 0;
  852. size_t startOfStringsToCompress = 0;
  853. if (m_cvarLocalizationEncode == 1)
  854. {
  855. {
  856. for (iEncoder = 0; iEncoder < m_pLanguage->m_vEncoders.size(); iEncoder++)
  857. {
  858. if (m_pLanguage->m_vEncoders[iEncoder] == NULL)
  859. {
  860. m_pLanguage->m_vEncoders[iEncoder] = pEncoder = new HuffmanCoder();
  861. break;
  862. }
  863. }
  864. if (iEncoder == m_pLanguage->m_vEncoders.size())
  865. {
  866. pEncoder = new HuffmanCoder();
  867. m_pLanguage->m_vEncoders.push_back(pEncoder);
  868. }
  869. //Make a note of the current end of the loc strings array, as encoding is done in two passes.
  870. //One pass to build the code table, another to apply it
  871. pEncoder->Init();
  872. }
  873. startOfStringsToCompress = m_pLanguage->m_vLocalizedStrings.size();
  874. }
  875. {
  876. if (!pXmlTableReader->Begin(root))
  877. {
  878. CryLog("Loading Localization File %s failed! The file is in an unsupported format.", sPath.c_str());
  879. pXmlTableReader->Release();
  880. return false;
  881. }
  882. }
  883. int rowCount = pXmlTableReader->GetEstimatedRowCount();
  884. {
  885. AutoLock lock(m_cs); //Make sure to lock, as this is a modifying operation
  886. m_pLanguage->m_vLocalizedStrings.reserve(m_pLanguage->m_vLocalizedStrings.size() + rowCount);
  887. }
  888. {
  889. AutoLock lock(m_cs); //Make sure to lock, as this is a modifying operation
  890. //VectorMap only, not applicable to std::map
  891. m_pLanguage->m_keysMap.reserve(m_pLanguage->m_keysMap.size() + rowCount);
  892. }
  893. {
  894. pairFileName sNewFile;
  895. sNewFile.first = sFileName;
  896. sNewFile.second.bDataStripping = false; // this is off for now
  897. sNewFile.second.nTagID = nTagID;
  898. m_loadedTables.insert(sNewFile);
  899. }
  900. // Cell Index
  901. char nCellIndexToType[MAX_CELL_COUNT];
  902. memset(nCellIndexToType, 0, sizeof(nCellIndexToType));
  903. // SoundMood Index
  904. std::map<int, AZStd::string> SoundMoodIndex;
  905. // EventParameter Index
  906. std::map<int, AZStd::string> EventParameterIndex;
  907. bool bFirstRow = true;
  908. AZStd::fixed_string<LOADING_FIXED_STRING_LENGTH> sTmp;
  909. // lower case event name
  910. char szLowerCaseEvent[128];
  911. // lower case key
  912. char szLowerCaseKey[1024];
  913. // key CRC
  914. uint32 keyCRC;
  915. for (;; )
  916. {
  917. int nRowIndex = -1;
  918. {
  919. if (!pXmlTableReader->ReadRow(nRowIndex))
  920. {
  921. break;
  922. }
  923. }
  924. if (bFirstRow)
  925. {
  926. bFirstRow = false;
  927. ParseFirstLine(pXmlTableReader, nCellIndexToType, SoundMoodIndex, EventParameterIndex);
  928. // Skip first row, it contains description only.
  929. continue;
  930. }
  931. bool bValidKey = false;
  932. bool bValidTranslatedText = false;
  933. bool bValidTranslatedCharacterName = false;
  934. bool bValidTranslatedActorLine = false;
  935. bool bUseSubtitle = true;
  936. bool bIsDirectRadio = false;
  937. bool bIsIntercepted = false;
  938. struct CConstCharArray
  939. {
  940. const char* ptr;
  941. size_t count;
  942. CConstCharArray()
  943. {
  944. clear();
  945. }
  946. void clear()
  947. {
  948. ptr = "";
  949. count = 0;
  950. }
  951. bool empty() const
  952. {
  953. return count == 0;
  954. }
  955. };
  956. CConstCharArray sKeyString;
  957. CConstCharArray sCharacterName;
  958. CConstCharArray sTranslatedCharacterName; // Legacy, to be removed some day...
  959. CConstCharArray sSubtitleText;
  960. CConstCharArray sTranslatedText; // Legacy, to be removed some day...
  961. CConstCharArray sActorLine;
  962. CConstCharArray sTranslatedActorLine; // Legacy, to be removed some day...
  963. CConstCharArray sSoundEvent;
  964. float fVolume = 1.0f;
  965. float fRadioRatio = 1.0f;
  966. float fEventParameterValue = 0.0f;
  967. float fSoundMoodValue = 0.0f;
  968. int nItems = 0;
  969. std::map<int, float> SoundMoodValues;
  970. std::map<int, float> EventParameterValues;
  971. for (;; )
  972. {
  973. int nCellIndex = -1;
  974. CConstCharArray cell;
  975. {
  976. if (!pXmlTableReader->ReadCell(nCellIndex, cell.ptr, cell.count))
  977. {
  978. break;
  979. }
  980. }
  981. if (nCellIndex >= MAX_CELL_COUNT)
  982. {
  983. break;
  984. }
  985. // skip empty cells
  986. if (cell.count <= 0)
  987. {
  988. continue;
  989. }
  990. const char nCellType = nCellIndexToType[nCellIndex];
  991. switch (nCellType)
  992. {
  993. case ELOCALIZED_COLUMN_SKIP:
  994. break;
  995. case ELOCALIZED_COLUMN_KEY:
  996. sKeyString = cell;
  997. bValidKey = true;
  998. ++nItems;
  999. break;
  1000. case ELOCALIZED_COLUMN_AUDIOFILE:
  1001. sKeyString = cell;
  1002. bValidKey = true;
  1003. ++nItems;
  1004. break;
  1005. case ELOCALIZED_COLUMN_CHARACTER_NAME:
  1006. sCharacterName = cell;
  1007. ++nItems;
  1008. break;
  1009. case ELOCALIZED_COLUMN_SUBTITLE_TEXT:
  1010. sSubtitleText = cell;
  1011. ++nItems;
  1012. break;
  1013. case ELOCALIZED_COLUMN_ACTOR_LINE:
  1014. sActorLine = cell;
  1015. ++nItems;
  1016. break;
  1017. case ELOCALIZED_COLUMN_USE_SUBTITLE:
  1018. sTmp.assign(cell.ptr, cell.count);
  1019. bUseSubtitle = ToYesNoType(sTmp.c_str()) == YesNoType::No ? false : true; // favor yes (yes and invalid -> yes)
  1020. break;
  1021. case ELOCALIZED_COLUMN_VOLUME:
  1022. sTmp.assign(cell.ptr, cell.count);
  1023. fVolume = (float)atof(sTmp.c_str());
  1024. ++nItems;
  1025. break;
  1026. case ELOCALIZED_COLUMN_SOUNDEVENT:
  1027. sSoundEvent = cell;
  1028. ++nItems;
  1029. break;
  1030. case ELOCALIZED_COLUMN_RADIO_RATIO:
  1031. sTmp.assign(cell.ptr, cell.count);
  1032. fRadioRatio = (float)atof(sTmp.c_str());
  1033. ++nItems;
  1034. break;
  1035. case ELOCALIZED_COLUMN_EVENTPARAMETER:
  1036. sTmp.assign(cell.ptr, cell.count);
  1037. fEventParameterValue = (float)atof(sTmp.c_str());
  1038. {
  1039. EventParameterValues[nCellIndex] = fEventParameterValue;
  1040. }
  1041. ++nItems;
  1042. break;
  1043. case ELOCALIZED_COLUMN_SOUNDMOOD:
  1044. sTmp.assign(cell.ptr, cell.count);
  1045. fSoundMoodValue = (float)atof(sTmp.c_str());
  1046. {
  1047. SoundMoodValues[nCellIndex] = fSoundMoodValue;
  1048. }
  1049. ++nItems;
  1050. break;
  1051. case ELOCALIZED_COLUMN_IS_DIRECT_RADIO:
  1052. sTmp.assign(cell.ptr, cell.count);
  1053. if (!_stricmp(sTmp.c_str(), "intercept"))
  1054. {
  1055. bIsIntercepted = true;
  1056. }
  1057. bIsDirectRadio = bIsIntercepted || (ToYesNoType(sTmp.c_str()) == YesNoType::Yes ? true : false); // favor no (no and invalid -> no)
  1058. ++nItems;
  1059. break;
  1060. // legacy names
  1061. case ELOCALIZED_COLUMN_LEGACY_PERSON:
  1062. // old file often only have content in this column
  1063. if (!cell.empty())
  1064. {
  1065. sCharacterName = cell;
  1066. sTranslatedCharacterName = cell;
  1067. bValidTranslatedCharacterName = true;
  1068. }
  1069. ++nItems;
  1070. break;
  1071. case ELOCALIZED_COLUMN_LEGACY_CHARACTERNAME:
  1072. sCharacterName = cell;
  1073. sTranslatedCharacterName = cell;
  1074. bValidTranslatedCharacterName = true;
  1075. ++nItems;
  1076. break;
  1077. case ELOCALIZED_COLUMN_LEGACY_TRANSLATED_CHARACTERNAME:
  1078. sTranslatedCharacterName = cell;
  1079. bValidTranslatedCharacterName = true;
  1080. ++nItems;
  1081. break;
  1082. case ELOCALIZED_COLUMN_LEGACY_ENGLISH_DIALOGUE:
  1083. // old file often only have content in this column
  1084. sActorLine = cell;
  1085. sSubtitleText = cell;
  1086. ++nItems;
  1087. break;
  1088. case ELOCALIZED_COLUMN_LEGACY_TRANSLATION:
  1089. sTranslatedActorLine = cell;
  1090. sTranslatedText = cell;
  1091. bValidTranslatedText = true;
  1092. ++nItems;
  1093. break;
  1094. case ELOCALIZED_COLUMN_LEGACY_YOUR_TRANSLATION:
  1095. sTranslatedActorLine = cell;
  1096. sTranslatedText = cell;
  1097. bValidTranslatedText = true;
  1098. ++nItems;
  1099. break;
  1100. case ELOCALIZED_COLUMN_LEGACY_ENGLISH_SUBTITLE:
  1101. sSubtitleText = cell;
  1102. ++nItems;
  1103. break;
  1104. case ELOCALIZED_COLUMN_LEGACY_TRANSLATED_SUBTITLE:
  1105. sTranslatedText = cell;
  1106. sTranslatedActorLine = cell;
  1107. bValidTranslatedText = true;
  1108. ++nItems;
  1109. break;
  1110. case ELOCALIZED_COLUMN_LEGACY_ORIGINAL_CHARACTER_NAME:
  1111. sCharacterName = cell;
  1112. ++nItems;
  1113. break;
  1114. case ELOCALIZED_COLUMN_LEGACY_TRANSLATED_CHARACTER_NAME:
  1115. sTranslatedCharacterName = cell;
  1116. bValidTranslatedCharacterName = true;
  1117. ++nItems;
  1118. break;
  1119. case ELOCALIZED_COLUMN_LEGACY_ORIGINAL_TEXT:
  1120. sSubtitleText = cell;
  1121. ++nItems;
  1122. break;
  1123. case ELOCALIZED_COLUMN_LEGACY_TRANSLATED_TEXT:
  1124. sTranslatedText = cell;
  1125. bValidTranslatedText = true;
  1126. ++nItems;
  1127. break;
  1128. case ELOCALIZED_COLUMN_LEGACY_ORIGINAL_ACTOR_LINE:
  1129. sActorLine = cell;
  1130. ++nItems;
  1131. break;
  1132. case ELOCALIZED_COLUMN_LEGACY_TRANSLATED_ACTOR_LINE:
  1133. sTranslatedActorLine = cell;
  1134. bValidTranslatedActorLine = true;
  1135. ++nItems;
  1136. break;
  1137. }
  1138. }
  1139. if (!bValidKey)
  1140. {
  1141. continue;
  1142. }
  1143. if (!bValidTranslatedText)
  1144. {
  1145. // if this is a dialog entry with a soundevent and with subtitles then a warning should be issued
  1146. if (m_cvarLocalizationDebug && !sSoundEvent.empty() && bUseSubtitle)
  1147. {
  1148. sTmp.assign(sKeyString.ptr, sKeyString.count);
  1149. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "[LocError] Key '%s' in file <%s> has no translated text", sTmp.c_str(), sFileName);
  1150. }
  1151. // use translated actor line entry if available before falling back to original entry
  1152. if (!sTranslatedActorLine.empty())
  1153. {
  1154. sTranslatedText = sTranslatedActorLine;
  1155. }
  1156. else
  1157. {
  1158. sTranslatedText = sSubtitleText;
  1159. }
  1160. }
  1161. if (!bValidTranslatedActorLine)
  1162. {
  1163. // if this is a dialog entry with a soundevent then a warning should be issued
  1164. if (m_cvarLocalizationDebug && !sSoundEvent.empty())
  1165. {
  1166. sTmp.assign(sKeyString.ptr, sKeyString.count);
  1167. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "[LocError] Key '%s' in file <%s> has no translated actor line", sTmp.c_str(), sFileName);
  1168. }
  1169. // use translated text entry if available before falling back to original entry
  1170. if (!sTranslatedText.empty())
  1171. {
  1172. sTranslatedActorLine = sTranslatedText;
  1173. }
  1174. else
  1175. {
  1176. sTranslatedActorLine = sSubtitleText;
  1177. }
  1178. }
  1179. if (!sSoundEvent.empty() && !bValidTranslatedCharacterName)
  1180. {
  1181. if (m_cvarLocalizationDebug)
  1182. {
  1183. sTmp.assign(sKeyString.ptr, sKeyString.count);
  1184. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "[LocError] Key '%s' in file <%s> has no translated character name", sTmp.c_str(), sFileName);
  1185. }
  1186. sTranslatedCharacterName = sCharacterName;
  1187. }
  1188. if (nItems == 1) // skip lines which contain just one item in the key
  1189. {
  1190. continue;
  1191. }
  1192. // reject to store text if line was marked with no subtitles in game mode
  1193. if (!gEnv->IsEditor())
  1194. {
  1195. if (!bUseSubtitle)
  1196. {
  1197. sSubtitleText.clear();
  1198. sTranslatedText.clear();
  1199. }
  1200. }
  1201. // Skip @ character in the key string.
  1202. if (!sKeyString.empty() && sKeyString.ptr[0] == '@')
  1203. {
  1204. sKeyString.ptr++;
  1205. sKeyString.count--;
  1206. }
  1207. {
  1208. CopyLowercase(szLowerCaseEvent, sizeof(szLowerCaseEvent), sSoundEvent.ptr, sSoundEvent.count);
  1209. CopyLowercase(szLowerCaseKey, sizeof(szLowerCaseKey), sKeyString.ptr, sKeyString.count);
  1210. }
  1211. //Compute the CRC32 of the key
  1212. keyCRC = AZ::Crc32(szLowerCaseKey);
  1213. if (m_cvarLocalizationDebug >= 3)
  1214. {
  1215. CryLogAlways("<Localization dupe/clash detection> CRC32: 0x%8X, Key: %s", keyCRC, szLowerCaseKey);
  1216. }
  1217. if (m_pLanguage->m_keysMap.find(keyCRC) != m_pLanguage->m_keysMap.end())
  1218. {
  1219. sTmp.assign(sKeyString.ptr, sKeyString.count);
  1220. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "[LocError] Localized String '%s' Already Loaded for Language %s OR there is a CRC hash clash", sTmp.c_str(), m_pLanguage->sLanguage.c_str());
  1221. continue;
  1222. }
  1223. SLocalizedStringEntry* pEntry = new SLocalizedStringEntry();
  1224. pEntry->flags = 0;
  1225. if (bUseSubtitle == true)
  1226. {
  1227. pEntry->flags |= SLocalizedStringEntry::USE_SUBTITLE;
  1228. }
  1229. pEntry->nTagID = nTagID;
  1230. if (gEnv->IsEditor())
  1231. {
  1232. pEntry->pEditorExtension = new SLocalizedStringEntryEditorExtension();
  1233. pEntry->pEditorExtension->sKey = szLowerCaseKey;
  1234. pEntry->pEditorExtension->nRow = nRowIndex;
  1235. if (!sActorLine.empty())
  1236. {
  1237. sTmp.assign(sActorLine.ptr, sActorLine.count);
  1238. ReplaceEndOfLine(sTmp);
  1239. pEntry->pEditorExtension->sOriginalActorLine.assign(sTmp.c_str());
  1240. }
  1241. if (!sTranslatedActorLine.empty())
  1242. {
  1243. sTmp.assign(sTranslatedActorLine.ptr, sTranslatedActorLine.count);
  1244. ReplaceEndOfLine(sTmp);
  1245. pEntry->pEditorExtension->sUtf8TranslatedActorLine.append(sTmp.c_str());
  1246. }
  1247. if (bUseSubtitle && !sSubtitleText.empty())
  1248. {
  1249. sTmp.assign(sSubtitleText.ptr, sSubtitleText.count);
  1250. ReplaceEndOfLine(sTmp);
  1251. pEntry->pEditorExtension->sOriginalText.assign(sTmp.c_str());
  1252. }
  1253. // only use the translated character name
  1254. {
  1255. pEntry->pEditorExtension->sOriginalCharacterName.assign(sCharacterName.ptr, sCharacterName.count);
  1256. }
  1257. }
  1258. if (bUseSubtitle && !sTranslatedText.empty())
  1259. {
  1260. sTmp.assign(sTranslatedText.ptr, sTranslatedText.count);
  1261. ReplaceEndOfLine(sTmp);
  1262. if (m_cvarLocalizationEncode == 1)
  1263. {
  1264. pEncoder->Update((const uint8*)(sTmp.c_str()), sTmp.length());
  1265. //CryLogAlways("%u Storing %s (%u)", m_pLanguage->m_vLocalizedStrings.size(), sTmp.c_str(), sTmp.length());
  1266. pEntry->TranslatedText.szCompressed = new uint8[sTmp.length() + 1];
  1267. pEntry->flags |= SLocalizedStringEntry::IS_COMPRESSED;
  1268. //Store the raw string. It'll be compressed later
  1269. memcpy(pEntry->TranslatedText.szCompressed, sTmp.c_str(), sTmp.length());
  1270. pEntry->TranslatedText.szCompressed[sTmp.length()] = '\0'; //Null terminate
  1271. }
  1272. else
  1273. {
  1274. pEntry->TranslatedText.psUtf8Uncompressed = new AZStd::string(sTmp.c_str(), sTmp.c_str() + sTmp.length());
  1275. }
  1276. }
  1277. // the following is used to cleverly assign strings
  1278. // we store all known string into the m_prototypeEvents set and assign known entries from there
  1279. // the CryString makes sure, that only the ref-count is increment on assignment
  1280. if (*szLowerCaseEvent)
  1281. {
  1282. PrototypeSoundEvents::iterator it = m_prototypeEvents.find(AZStd::string(szLowerCaseEvent));
  1283. if (it != m_prototypeEvents.end())
  1284. {
  1285. pEntry->sPrototypeSoundEvent = *it;
  1286. }
  1287. else
  1288. {
  1289. pEntry->sPrototypeSoundEvent = szLowerCaseEvent;
  1290. m_prototypeEvents.insert(pEntry->sPrototypeSoundEvent);
  1291. }
  1292. }
  1293. const CConstCharArray sWho = sTranslatedCharacterName.empty() ? sCharacterName : sTranslatedCharacterName;
  1294. if (!sWho.empty())
  1295. {
  1296. sTmp.assign(sWho.ptr, sWho.count);
  1297. ReplaceEndOfLine(sTmp);
  1298. AZStd::replace(sTmp.begin(), sTmp.end(), ' ', '_');
  1299. AZStd::string tmp;
  1300. {
  1301. tmp = sTmp.c_str();
  1302. }
  1303. CharacterNameSet::iterator it = m_characterNameSet.find(tmp);
  1304. if (it != m_characterNameSet.end())
  1305. {
  1306. pEntry->sCharacterName = *it;
  1307. }
  1308. else
  1309. {
  1310. pEntry->sCharacterName = tmp;
  1311. m_characterNameSet.insert(pEntry->sCharacterName);
  1312. }
  1313. }
  1314. pEntry->fVolume = CryConvertFloatToHalf(fVolume);
  1315. // SoundMood Entries
  1316. {
  1317. pEntry->SoundMoods.resize(static_cast<int>(SoundMoodValues.size()));
  1318. if (SoundMoodValues.size() > 0)
  1319. {
  1320. std::map<int, float>::const_iterator itEnd = SoundMoodValues.end();
  1321. int nSoundMoodCount = 0;
  1322. for (std::map<int, float>::const_iterator it = SoundMoodValues.begin(); it != itEnd; ++it)
  1323. {
  1324. pEntry->SoundMoods[nSoundMoodCount].fValue = (*it).second;
  1325. pEntry->SoundMoods[nSoundMoodCount].sName = SoundMoodIndex[(*it).first];
  1326. ++nSoundMoodCount;
  1327. }
  1328. }
  1329. }
  1330. // EventParameter Entries
  1331. {
  1332. pEntry->EventParameters.resize(static_cast<int>(EventParameterValues.size()));
  1333. if (EventParameterValues.size() > 0)
  1334. {
  1335. std::map<int, float>::const_iterator itEnd = EventParameterValues.end();
  1336. int nEventParameterCount = 0;
  1337. for (std::map<int, float>::const_iterator it = EventParameterValues.begin(); it != itEnd; ++it)
  1338. {
  1339. pEntry->EventParameters[nEventParameterCount].fValue = (*it).second;
  1340. pEntry->EventParameters[nEventParameterCount].sName = EventParameterIndex[(*it).first];
  1341. ++nEventParameterCount;
  1342. }
  1343. }
  1344. }
  1345. pEntry->fRadioRatio = CryConvertFloatToHalf(fRadioRatio);
  1346. if (bIsDirectRadio == true)
  1347. {
  1348. pEntry->flags |= SLocalizedStringEntry::IS_DIRECTED_RADIO;
  1349. }
  1350. if (bIsIntercepted == true)
  1351. {
  1352. pEntry->flags |= SLocalizedStringEntry::IS_INTERCEPTED;
  1353. }
  1354. AddLocalizedString(m_pLanguage, pEntry, keyCRC);
  1355. }
  1356. if (m_cvarLocalizationEncode == 1)
  1357. {
  1358. pEncoder->Finalize();
  1359. uint8 compressionBuffer[COMPRESSION_FIXED_BUFFER_LENGTH];
  1360. for (size_t stringToCompress = startOfStringsToCompress; stringToCompress < m_pLanguage->m_vLocalizedStrings.size(); stringToCompress++)
  1361. {
  1362. SLocalizedStringEntry* pStringToCompress = m_pLanguage->m_vLocalizedStrings[stringToCompress];
  1363. if (pStringToCompress->TranslatedText.szCompressed != NULL)
  1364. {
  1365. size_t compBufSize = COMPRESSION_FIXED_BUFFER_LENGTH;
  1366. memset(compressionBuffer, 0, COMPRESSION_FIXED_BUFFER_LENGTH);
  1367. size_t inputStringLength = strlen((const char*)(pStringToCompress->TranslatedText.szCompressed));
  1368. pEncoder->CompressInput(pStringToCompress->TranslatedText.szCompressed, inputStringLength, compressionBuffer, &compBufSize);
  1369. compressionBuffer[compBufSize] = 0;
  1370. pStringToCompress->huffmanTreeIndex = iEncoder;
  1371. pEncoder->AddRef();
  1372. uint8* szCompressedString = new uint8[compBufSize];
  1373. SAFE_DELETE_ARRAY(pStringToCompress->TranslatedText.szCompressed);
  1374. memcpy(szCompressedString, compressionBuffer, compBufSize);
  1375. pStringToCompress->TranslatedText.szCompressed = szCompressedString;
  1376. }
  1377. }
  1378. }
  1379. pXmlTableReader->Release();
  1380. return true;
  1381. }
  1382. bool CLocalizedStringsManager::DoLoadAGSXmlDocument(const char* sFileName, uint8 nTagID, bool bReload)
  1383. {
  1384. if (!sFileName|| !m_pLanguage)
  1385. {
  1386. return false;
  1387. }
  1388. if (!bReload)
  1389. {
  1390. if (m_loadedTables.find(AZStd::string(sFileName)) != m_loadedTables.end())
  1391. {
  1392. return true;
  1393. }
  1394. }
  1395. ListAndClearProblemLabels();
  1396. XmlNodeRef root;
  1397. AZStd::string sPath;
  1398. {
  1399. const AZStd::string sLocalizationFolder(PathUtil::GetLocalizationRoot());
  1400. const AZStd::string& languageFolder = m_pLanguage->sLanguage;
  1401. sPath = sLocalizationFolder.c_str() + languageFolder + PathUtil::GetSlash() + sFileName;
  1402. root = m_pSystem->LoadXmlFromFile(sPath.c_str());
  1403. if (!root)
  1404. {
  1405. AZ_TracePrintf(LOC_WINDOW, "Loading Localization File %s failed!", sPath.c_str());
  1406. return false;
  1407. }
  1408. }
  1409. AZ_TracePrintf(LOC_WINDOW, "Loading Localization File %s", sPath.c_str());
  1410. HuffmanCoder* pEncoder = nullptr;
  1411. uint8 iEncoder = 0;
  1412. size_t startOfStringsToCompress = 0;
  1413. if (m_cvarLocalizationEncode == 1)
  1414. {
  1415. {
  1416. for (iEncoder = 0; iEncoder < m_pLanguage->m_vEncoders.size(); iEncoder++)
  1417. {
  1418. if (m_pLanguage->m_vEncoders[iEncoder] == nullptr)
  1419. {
  1420. pEncoder = new HuffmanCoder();
  1421. m_pLanguage->m_vEncoders[iEncoder] = pEncoder;
  1422. break;
  1423. }
  1424. }
  1425. if (iEncoder == m_pLanguage->m_vEncoders.size())
  1426. {
  1427. pEncoder = new HuffmanCoder();
  1428. m_pLanguage->m_vEncoders.push_back(pEncoder);
  1429. }
  1430. pEncoder->Init();
  1431. }
  1432. startOfStringsToCompress = m_pLanguage->m_vLocalizedStrings.size();
  1433. }
  1434. int rowCount = 0;
  1435. {
  1436. AutoLock lock(m_cs); // Make sure to lock, as this is a modifying operation
  1437. rowCount = root->getChildCount();
  1438. {
  1439. m_pLanguage->m_vLocalizedStrings.reserve(m_pLanguage->m_vLocalizedStrings.size() + rowCount);
  1440. }
  1441. m_pLanguage->m_keysMap.reserve(m_pLanguage->m_keysMap.size() + rowCount);
  1442. }
  1443. {
  1444. pairFileName sNewFile;
  1445. sNewFile.first = sFileName;
  1446. sNewFile.second.bDataStripping = false; // this is off for now
  1447. sNewFile.second.nTagID = nTagID;
  1448. m_loadedTables.insert(sNewFile);
  1449. }
  1450. const char* key = nullptr;
  1451. AZStd::string keyString;
  1452. AZStd::string lowerKey;
  1453. AZStd::string textValue;
  1454. uint32 keyCRC=0;
  1455. for (int i = 0; i < rowCount; ++i)
  1456. {
  1457. XmlNodeRef childNode = root->getChild(i);
  1458. if (azstricmp(childNode->getTag(), "string") || !childNode->getAttr("key", &key))
  1459. {
  1460. continue;
  1461. }
  1462. keyString = key;
  1463. textValue = childNode->getContent();
  1464. if (textValue.empty())
  1465. {
  1466. continue;
  1467. }
  1468. AzFramework::StringFunc::Replace(textValue, "\\n", " \n");
  1469. if (keyString[0] == '@')
  1470. {
  1471. AzFramework::StringFunc::LChop(keyString, 1);
  1472. }
  1473. lowerKey = keyString;
  1474. AZStd::to_lower(lowerKey.begin(), lowerKey.end());
  1475. keyCRC = AZ::Crc32(lowerKey);
  1476. if (m_cvarLocalizationDebug >= 3)
  1477. {
  1478. CryLogAlways("<Localization dupe/clash detection> CRC32: 0%8X, Key: %s", keyCRC, lowerKey.c_str());
  1479. }
  1480. if (m_pLanguage->m_keysMap.find(keyCRC) != m_pLanguage->m_keysMap.end())
  1481. {
  1482. AZ_Warning(LOC_WINDOW, false, "Localized String '%s' Already Loaded for Language %s OR there is a CRC hash clash", keyString.c_str(), m_pLanguage->sLanguage.c_str());
  1483. continue;
  1484. }
  1485. SLocalizedStringEntry* pEntry = new SLocalizedStringEntry;
  1486. pEntry->flags = SLocalizedStringEntry::USE_SUBTITLE;
  1487. pEntry->nTagID = nTagID;
  1488. if (gEnv->IsEditor())
  1489. {
  1490. pEntry->pEditorExtension = new SLocalizedStringEntryEditorExtension();
  1491. pEntry->pEditorExtension->sKey = lowerKey.c_str();
  1492. pEntry->pEditorExtension->nRow = i;
  1493. {
  1494. pEntry->pEditorExtension->sUtf8TranslatedActorLine.append(textValue.c_str());
  1495. }
  1496. {
  1497. pEntry->pEditorExtension->sOriginalText.assign(textValue.c_str());
  1498. }
  1499. }
  1500. {
  1501. const char* textString = textValue.c_str();
  1502. size_t textLength = textValue.length();
  1503. if (m_cvarLocalizationEncode == 1)
  1504. {
  1505. pEncoder->Update((const uint8*)(textString), textLength);
  1506. pEntry->TranslatedText.szCompressed = new uint8[textLength + 1];
  1507. pEntry->flags |= SLocalizedStringEntry::IS_COMPRESSED;
  1508. memcpy(pEntry->TranslatedText.szCompressed, textString, textLength);
  1509. pEntry->TranslatedText.szCompressed[textLength] = '\0'; //Null terminate
  1510. }
  1511. else
  1512. {
  1513. pEntry->TranslatedText.psUtf8Uncompressed = new AZStd::string(textString, textString + textLength);
  1514. }
  1515. }
  1516. {
  1517. AddLocalizedString(m_pLanguage, pEntry, keyCRC);
  1518. }
  1519. }
  1520. if (m_cvarLocalizationEncode == 1)
  1521. {
  1522. {
  1523. pEncoder->Finalize();
  1524. }
  1525. {
  1526. uint8 compressionBuffer[COMPRESSION_FIXED_BUFFER_LENGTH] = {};
  1527. for (size_t stringToCompress = startOfStringsToCompress; stringToCompress < m_pLanguage->m_vLocalizedStrings.size(); stringToCompress++)
  1528. {
  1529. SLocalizedStringEntry* pStringToCompress = m_pLanguage->m_vLocalizedStrings[stringToCompress];
  1530. if (pStringToCompress->TranslatedText.szCompressed != nullptr)
  1531. {
  1532. size_t compBufSize = COMPRESSION_FIXED_BUFFER_LENGTH;
  1533. memset(compressionBuffer, 0, COMPRESSION_FIXED_BUFFER_LENGTH);
  1534. size_t inputStringLength = strnlen((const char*)(pStringToCompress->TranslatedText.szCompressed), COMPRESSION_FIXED_BUFFER_LENGTH);
  1535. pEncoder->CompressInput(pStringToCompress->TranslatedText.szCompressed, inputStringLength, compressionBuffer, &compBufSize);
  1536. compressionBuffer[compBufSize] = 0;
  1537. pStringToCompress->huffmanTreeIndex = iEncoder;
  1538. pEncoder->AddRef();
  1539. uint8* szCompressedString = new uint8[compBufSize];
  1540. SAFE_DELETE_ARRAY(pStringToCompress->TranslatedText.szCompressed);
  1541. memcpy(szCompressedString, compressionBuffer, compBufSize);
  1542. pStringToCompress->TranslatedText.szCompressed = szCompressedString;
  1543. }
  1544. }
  1545. }
  1546. }
  1547. return true;
  1548. }
  1549. //////////////////////////////////////////////////////////////////////////
  1550. CLocalizedStringsManager::LoadFunc CLocalizedStringsManager::GetLoadFunction() const
  1551. {
  1552. CRY_ASSERT_MESSAGE(gEnv && gEnv->pConsole, "System environment or console missing!");
  1553. if (gEnv && gEnv->pConsole)
  1554. {
  1555. int32_t localizationFormat{};
  1556. if (auto console = AZ::Interface<AZ::IConsole>::Get();
  1557. console !=nullptr)
  1558. {
  1559. console->GetCvarValue(c_sys_localization_format, localizationFormat);
  1560. }
  1561. if(localizationFormat == 1)
  1562. {
  1563. return &CLocalizedStringsManager::DoLoadAGSXmlDocument;
  1564. }
  1565. }
  1566. return &CLocalizedStringsManager::DoLoadExcelXmlSpreadsheet;
  1567. }
  1568. void CLocalizedStringsManager::ReloadData()
  1569. {
  1570. tmapFilenames temp = m_loadedTables;
  1571. LoadFunc loadFunction = GetLoadFunction();
  1572. FreeLocalizationData();
  1573. for (tmapFilenames::iterator it = temp.begin(); it != temp.end(); it++)
  1574. {
  1575. (this->*loadFunction)((*it).first.c_str(), (*it).second.nTagID, true);
  1576. }
  1577. }
  1578. //////////////////////////////////////////////////////////////////////////
  1579. void CLocalizedStringsManager::AddLocalizedString(SLanguage* pLanguage, SLocalizedStringEntry* pEntry, const uint32 keyCRC32)
  1580. {
  1581. pLanguage->m_vLocalizedStrings.push_back(pEntry);
  1582. [[maybe_unused]] int nId = (int)pLanguage->m_vLocalizedStrings.size() - 1;
  1583. pLanguage->m_keysMap[keyCRC32] = pEntry;
  1584. if (m_cvarLocalizationDebug >= 2)
  1585. {
  1586. CryLog("<Localization> Add new string <%u> with ID %d to <%s>", keyCRC32, nId, pLanguage->sLanguage.c_str());
  1587. }
  1588. }
  1589. //////////////////////////////////////////////////////////////////////////
  1590. bool CLocalizedStringsManager::LocalizeString_ch(const char* sString, AZStd::string& outLocalizedString, bool bEnglish)
  1591. {
  1592. return LocalizeStringInternal(sString, strlen(sString), outLocalizedString, bEnglish);
  1593. }
  1594. //////////////////////////////////////////////////////////////////////////
  1595. bool CLocalizedStringsManager::LocalizeString_s(const AZStd::string& sString, AZStd::string& outLocalizedString, bool bEnglish)
  1596. {
  1597. return LocalizeStringInternal(sString.c_str(), sString.length(), outLocalizedString, bEnglish);
  1598. }
  1599. //////////////////////////////////////////////////////////////////////////
  1600. bool CLocalizedStringsManager::LocalizeStringInternal(const char* pStr, size_t len, AZStd::string& outLocalizedString, bool bEnglish)
  1601. {
  1602. assert (m_pLanguage);
  1603. if (m_pLanguage == nullptr)
  1604. {
  1605. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "LocalizeString: No language set.");
  1606. outLocalizedString.assign(pStr, pStr + len);
  1607. return false;
  1608. }
  1609. // note: we don't write directly to outLocalizedString, in case it aliases pStr
  1610. AZStd::string out;
  1611. // scan the string
  1612. const char* pPos = pStr;
  1613. const char* pEnd = pStr + len;
  1614. while (true)
  1615. {
  1616. const char* pLabel = strchr(pPos, '@');
  1617. if (!pLabel)
  1618. {
  1619. break;
  1620. }
  1621. // found an occurrence
  1622. // we have skipped a few characters, so copy them over
  1623. if (pLabel != pPos)
  1624. {
  1625. out.append(pPos, pLabel);
  1626. }
  1627. // Search label for first occurence of any label terminating character
  1628. const char* pLabelEnd = strpbrk(pLabel, " !\"#$%&\'()*+,./:;<=>\?[\\]^`{|}~\n\t\r");
  1629. if (!pLabelEnd)
  1630. {
  1631. pLabelEnd = pEnd;
  1632. }
  1633. // localize token
  1634. AZStd::string token(pLabel, pLabelEnd);
  1635. AZStd::string sLocalizedToken;
  1636. if (bEnglish)
  1637. {
  1638. GetEnglishString(token.c_str(), sLocalizedToken);
  1639. }
  1640. else
  1641. {
  1642. LocalizeLabel(token.c_str(), sLocalizedToken);
  1643. }
  1644. out.append(sLocalizedToken);
  1645. pPos = pLabelEnd;
  1646. }
  1647. out.append(pPos, pEnd);
  1648. out.swap(outLocalizedString);
  1649. return true;
  1650. }
  1651. void CLocalizedStringsManager::LocalizeAndSubstituteInternal(AZStd::string& locString, const AZStd::vector<AZStd::string>& keys, const AZStd::vector<AZStd::string>& values)
  1652. {
  1653. AZStd::string outString;
  1654. LocalizeString_ch(locString.c_str(), outString);
  1655. locString = outString .c_str();
  1656. if (values.size() != keys.size())
  1657. {
  1658. AZ_Warning("game", false, "Localization Error: LocalizeAndSubstitute was given %u keys and %u values to replace. These numbers must be equal.", keys.size(), values.size());
  1659. return;
  1660. }
  1661. size_t startIndex = locString.find('{');
  1662. size_t endIndex = locString.find('}', startIndex);
  1663. while (startIndex != AZStd::string::npos && endIndex != AZStd::string::npos)
  1664. {
  1665. const size_t subLength = endIndex - startIndex - 1;
  1666. AZStd::string substituteOut = locString.substr(startIndex + 1, subLength).c_str();
  1667. int index = 0;
  1668. if (LocalizationHelpers::IsKeyInList(keys, substituteOut, index))
  1669. {
  1670. const char* value = values[index].c_str();
  1671. locString.replace(startIndex, subLength + 2, value);
  1672. startIndex += strlen(value);
  1673. }
  1674. else
  1675. {
  1676. AZ_Warning("game", false, "Localization Error: Localized string '%s' contains a key '%s' that is not mapped to a data element.", locString.c_str(), substituteOut.c_str());
  1677. startIndex += substituteOut.length();
  1678. }
  1679. startIndex = locString.find_first_of('{', startIndex);
  1680. endIndex = locString.find_first_of('}', startIndex);
  1681. }
  1682. }
  1683. #if defined(LOG_DECOMP_TIMES)
  1684. static double g_fSecondsPerTick = 0.0;
  1685. static FILE* pDecompLog = NULL;
  1686. // engine independent game timer since gEnv/pSystem isn't available yet
  1687. static void LogDecompTimer(__int64 nTotalTicks, __int64 nDecompTicks, __int64 nAllocTicks)
  1688. {
  1689. if (g_fSecondsPerTick == 0.0)
  1690. {
  1691. __int64 nPerfFreq;
  1692. QueryPerformanceFrequency((LARGE_INTEGER*)&nPerfFreq);
  1693. g_fSecondsPerTick = 1.0 / (double)nPerfFreq;
  1694. }
  1695. if (!pDecompLog)
  1696. {
  1697. char szFilenameBuffer[MAX_PATH];
  1698. time_t rawTime;
  1699. time(&rawTime);
  1700. struct tm* pTimeInfo = localtime(&rawTime);
  1701. CreateDirectory("TestResults\\", 0);
  1702. strftime(szFilenameBuffer, sizeof(szFilenameBuffer), "TestResults\\Decomp_%Y_%m_%d-%H_%M_%S.csv", pTimeInfo);
  1703. pDecompLog = fopen(szFilenameBuffer, "wb");
  1704. fprintf(pDecompLog, "Total,Decomp,Alloc\n");
  1705. }
  1706. float nTotalMillis = float( g_fSecondsPerTick * 1000.0 * nTotalTicks );
  1707. float nDecompMillis = float( g_fSecondsPerTick * 1000.0 * nDecompTicks );
  1708. float nAllocMillis = float( g_fSecondsPerTick * 1000.0 * nAllocTicks );
  1709. fprintf(pDecompLog, "%f,%f,%f\n", nTotalMillis, nDecompMillis, nAllocMillis);
  1710. fflush(pDecompLog);
  1711. }
  1712. #endif
  1713. AZStd::string CLocalizedStringsManager::SLocalizedStringEntry::GetTranslatedText(const SLanguage* pLanguage) const
  1714. {
  1715. if ((flags & IS_COMPRESSED) != 0)
  1716. {
  1717. #if defined(LOG_DECOMP_TIMES)
  1718. __int64 nTotalTicks, nDecompTicks, nAllocTicks;
  1719. nTotalTicks = CryGetTicks();
  1720. #endif //LOG_DECOMP_TIMES
  1721. AZStd::string outputString;
  1722. if (TranslatedText.szCompressed != NULL)
  1723. {
  1724. uint8 decompressionBuffer[COMPRESSION_FIXED_BUFFER_LENGTH];
  1725. HuffmanCoder* pEncoder = pLanguage->m_vEncoders[huffmanTreeIndex];
  1726. #if defined(LOG_DECOMP_TIMES)
  1727. nDecompTicks = CryGetTicks();
  1728. #endif //LOG_DECOMP_TIMES
  1729. //We don't actually know how much memory was allocated for this string, but the maximum compression buffer size is known
  1730. size_t decompBufSize = pEncoder->UncompressInput(TranslatedText.szCompressed, COMPRESSION_FIXED_BUFFER_LENGTH, decompressionBuffer, COMPRESSION_FIXED_BUFFER_LENGTH);
  1731. assert(decompBufSize < COMPRESSION_FIXED_BUFFER_LENGTH && "Buffer overflow");
  1732. #if defined(LOG_DECOMP_TIMES)
  1733. nDecompTicks = CryGetTicks() - nDecompTicks;
  1734. #endif //LOG_DECOMP_TIMES
  1735. #if !defined(NDEBUG)
  1736. size_t len = strnlen((const char*)decompressionBuffer, COMPRESSION_FIXED_BUFFER_LENGTH);
  1737. assert(len < COMPRESSION_FIXED_BUFFER_LENGTH && "Buffer not null-terminated");
  1738. #endif
  1739. #if defined(LOG_DECOMP_TIMES)
  1740. nAllocTicks = CryGetTicks();
  1741. #endif //LOG_DECOMP_TIMES
  1742. outputString.assign((const char*)decompressionBuffer, (const char*)decompressionBuffer + decompBufSize);
  1743. #if defined(LOG_DECOMP_TIMES)
  1744. nAllocTicks = CryGetTicks() - nAllocTicks;
  1745. nTotalTicks = CryGetTicks() - nTotalTicks;
  1746. LogDecompTimer(nTotalTicks, nDecompTicks, nAllocTicks);
  1747. #endif //LOG_DECOMP_TIMES
  1748. }
  1749. return outputString;
  1750. }
  1751. else
  1752. {
  1753. if (TranslatedText.psUtf8Uncompressed != NULL)
  1754. {
  1755. return *TranslatedText.psUtf8Uncompressed;
  1756. }
  1757. else
  1758. {
  1759. AZStd::string emptyOutputString;
  1760. return emptyOutputString;
  1761. }
  1762. }
  1763. }
  1764. #ifndef _RELEASE
  1765. //////////////////////////////////////////////////////////////////////////
  1766. void CLocalizedStringsManager::LocalizedStringsManagerWarning(const char* label, const char* message)
  1767. {
  1768. if (!m_warnedAboutLabels[label])
  1769. {
  1770. CryWarning (VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Failed to localize label '%s' - %s", label, message);
  1771. m_warnedAboutLabels[label] = true;
  1772. m_haveWarnedAboutAtLeastOneLabel = true;
  1773. }
  1774. }
  1775. //////////////////////////////////////////////////////////////////////////
  1776. void CLocalizedStringsManager::ListAndClearProblemLabels()
  1777. {
  1778. if (m_haveWarnedAboutAtLeastOneLabel)
  1779. {
  1780. CryLog ("These labels caused localization problems:");
  1781. INDENT_LOG_DURING_SCOPE();
  1782. for (std::map<AZStd::string, bool>::iterator iter = m_warnedAboutLabels.begin(); iter != m_warnedAboutLabels.end(); iter++)
  1783. {
  1784. CryLog ("%s", iter->first.c_str());
  1785. }
  1786. m_warnedAboutLabels.clear();
  1787. m_haveWarnedAboutAtLeastOneLabel = false;
  1788. }
  1789. }
  1790. #endif
  1791. //////////////////////////////////////////////////////////////////////////
  1792. bool CLocalizedStringsManager::LocalizeLabel(const char* sLabel, AZStd::string& outLocalString, bool bEnglish)
  1793. {
  1794. assert(sLabel);
  1795. if (!m_pLanguage || !sLabel)
  1796. {
  1797. return false;
  1798. }
  1799. // Label sign.
  1800. if (sLabel[0] == '@')
  1801. {
  1802. uint32 labelCRC32 = AZ::Crc32(sLabel + 1); // skip @ character.
  1803. {
  1804. AutoLock lock(m_cs); //Lock here, to prevent strings etc being modified underneath this lookup
  1805. SLocalizedStringEntry* entry = stl::find_in_map(m_pLanguage->m_keysMap, labelCRC32, NULL);
  1806. if (entry != NULL)
  1807. {
  1808. AZStd::string translatedText = entry->GetTranslatedText(m_pLanguage);
  1809. if ((bEnglish || translatedText.empty()) && entry->pEditorExtension != NULL)
  1810. {
  1811. //assert(!"No Localization Text available!");
  1812. outLocalString.assign(entry->pEditorExtension->sOriginalText);
  1813. return true;
  1814. }
  1815. else
  1816. {
  1817. outLocalString.swap(translatedText);
  1818. }
  1819. return true;
  1820. }
  1821. else
  1822. {
  1823. LocalizedStringsManagerWarning(sLabel, "entry not found in string table");
  1824. }
  1825. }
  1826. }
  1827. else
  1828. {
  1829. LocalizedStringsManagerWarning(sLabel, "must start with @ symbol");
  1830. }
  1831. outLocalString.assign(sLabel);
  1832. return false;
  1833. }
  1834. //////////////////////////////////////////////////////////////////////////
  1835. bool CLocalizedStringsManager::GetEnglishString(const char* sKey, AZStd::string& sLocalizedString)
  1836. {
  1837. assert(sKey);
  1838. if (!m_pLanguage || !sKey)
  1839. {
  1840. return false;
  1841. }
  1842. // Label sign.
  1843. if (sKey[0] == '@')
  1844. {
  1845. uint32 keyCRC32 = AZ::Crc32(sKey + 1);
  1846. {
  1847. AutoLock lock(m_cs); // Lock here, to prevent strings etc being modified underneath this lookup
  1848. SLocalizedStringEntry* entry = stl::find_in_map(m_pLanguage->m_keysMap, keyCRC32, NULL); // skip @ character.
  1849. if (entry != NULL && entry->pEditorExtension != NULL)
  1850. {
  1851. sLocalizedString = entry->pEditorExtension->sOriginalText;
  1852. return true;
  1853. }
  1854. else
  1855. {
  1856. keyCRC32 = AZ::Crc32(sKey);
  1857. entry = stl::find_in_map(m_pLanguage->m_keysMap, keyCRC32, NULL);
  1858. if (entry != NULL && entry->pEditorExtension != NULL)
  1859. {
  1860. sLocalizedString = entry->pEditorExtension->sOriginalText;
  1861. return true;
  1862. }
  1863. else
  1864. {
  1865. // CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"Localized string for Label <%s> not found", sKey );
  1866. sLocalizedString = sKey;
  1867. return false;
  1868. }
  1869. }
  1870. }
  1871. }
  1872. else
  1873. {
  1874. // CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"Not a valid localized string Label <%s>, must start with @ symbol", sKey
  1875. // );
  1876. }
  1877. sLocalizedString = sKey;
  1878. return false;
  1879. }
  1880. bool CLocalizedStringsManager::IsLocalizedInfoFound(const char* sKey)
  1881. {
  1882. if (!m_pLanguage)
  1883. {
  1884. return false;
  1885. }
  1886. uint32 keyCRC32 = AZ::Crc32(sKey);
  1887. {
  1888. AutoLock lock(m_cs); //Lock here, to prevent strings etc being modified underneath this lookup
  1889. const SLocalizedStringEntry* entry = stl::find_in_map(m_pLanguage->m_keysMap, keyCRC32, NULL);
  1890. return (entry != NULL);
  1891. }
  1892. }
  1893. //////////////////////////////////////////////////////////////////////////
  1894. bool CLocalizedStringsManager::GetLocalizedInfoByKey(const char* sKey, SLocalizedInfoGame& outGameInfo)
  1895. {
  1896. if (!m_pLanguage)
  1897. {
  1898. return false;
  1899. }
  1900. uint32 keyCRC32 = AZ::Crc32(sKey);
  1901. {
  1902. AutoLock lock(m_cs); //Lock here, to prevent strings etc being modified underneath this lookup
  1903. const SLocalizedStringEntry* entry = stl::find_in_map(m_pLanguage->m_keysMap, keyCRC32, NULL);
  1904. if (entry != NULL)
  1905. {
  1906. outGameInfo.szCharacterName = entry->sCharacterName.c_str();
  1907. outGameInfo.sUtf8TranslatedText = entry->GetTranslatedText(m_pLanguage);
  1908. outGameInfo.bUseSubtitle = (entry->flags & SLocalizedStringEntry::USE_SUBTITLE);
  1909. return true;
  1910. }
  1911. else
  1912. {
  1913. return false;
  1914. }
  1915. }
  1916. }
  1917. //////////////////////////////////////////////////////////////////////////
  1918. bool CLocalizedStringsManager::GetLocalizedInfoByKey(const char* sKey, SLocalizedSoundInfoGame* pOutSoundInfo)
  1919. {
  1920. assert(sKey);
  1921. if (!m_pLanguage || !sKey || !pOutSoundInfo)
  1922. {
  1923. return false;
  1924. }
  1925. bool bResult = false;
  1926. uint32 keyCRC32 = AZ::Crc32(sKey);
  1927. {
  1928. AutoLock lock(m_cs); //Lock here, to prevent strings etc being modified underneath this lookup
  1929. const SLocalizedStringEntry* pEntry = stl::find_in_map(m_pLanguage->m_keysMap, keyCRC32, NULL);
  1930. if (pEntry != NULL)
  1931. {
  1932. bResult = true;
  1933. pOutSoundInfo->szCharacterName = pEntry->sCharacterName.c_str();
  1934. pOutSoundInfo->sUtf8TranslatedText = pEntry->GetTranslatedText(m_pLanguage);
  1935. //pOutSoundInfo->sOriginalActorLine = pEntry->sOriginalActorLine.c_str();
  1936. //pOutSoundInfo->sTranslatedActorLine = pEntry->swTranslatedActorLine.c_str();
  1937. //pOutSoundInfo->sOriginalText = pEntry->sOriginalText;
  1938. // original Character
  1939. pOutSoundInfo->sSoundEvent = pEntry->sPrototypeSoundEvent.c_str();
  1940. pOutSoundInfo->fVolume = CryConvertHalfToFloat(pEntry->fVolume);
  1941. pOutSoundInfo->fRadioRatio = CryConvertHalfToFloat(pEntry->fRadioRatio);
  1942. pOutSoundInfo->bUseSubtitle = (pEntry->flags & SLocalizedStringEntry::USE_SUBTITLE) != 0;
  1943. pOutSoundInfo->bIsDirectRadio = (pEntry->flags & SLocalizedStringEntry::IS_DIRECTED_RADIO) != 0;
  1944. pOutSoundInfo->bIsIntercepted = (pEntry->flags & SLocalizedStringEntry::IS_INTERCEPTED) != 0;
  1945. //SoundMoods
  1946. if (pOutSoundInfo->nNumSoundMoods >= pEntry->SoundMoods.size())
  1947. {
  1948. // enough space to copy data
  1949. int i = 0;
  1950. for (; i < pEntry->SoundMoods.size(); ++i)
  1951. {
  1952. pOutSoundInfo->pSoundMoods[i].sName = pEntry->SoundMoods[i].sName;
  1953. pOutSoundInfo->pSoundMoods[i].fValue = pEntry->SoundMoods[i].fValue;
  1954. }
  1955. // if mode is available fill it with default
  1956. for (; i < pOutSoundInfo->nNumSoundMoods; ++i)
  1957. {
  1958. pOutSoundInfo->pSoundMoods[i].sName = "";
  1959. pOutSoundInfo->pSoundMoods[i].fValue = 0.0f;
  1960. }
  1961. pOutSoundInfo->nNumSoundMoods = pEntry->SoundMoods.size();
  1962. }
  1963. else
  1964. {
  1965. // not enough memory, say what is needed
  1966. pOutSoundInfo->nNumSoundMoods = pEntry->SoundMoods.size();
  1967. bResult = (pOutSoundInfo->pSoundMoods == NULL); // only report error if memory was provided but is too small
  1968. }
  1969. //EventParameters
  1970. if (pOutSoundInfo->nNumEventParameters >= pEntry->EventParameters.size())
  1971. {
  1972. // enough space to copy data
  1973. int i = 0;
  1974. for (; i < pEntry->EventParameters.size(); ++i)
  1975. {
  1976. pOutSoundInfo->pEventParameters[i].sName = pEntry->EventParameters[i].sName;
  1977. pOutSoundInfo->pEventParameters[i].fValue = pEntry->EventParameters[i].fValue;
  1978. }
  1979. // if mode is available fill it with default
  1980. for (; i < pOutSoundInfo->nNumEventParameters; ++i)
  1981. {
  1982. pOutSoundInfo->pEventParameters[i].sName = "";
  1983. pOutSoundInfo->pEventParameters[i].fValue = 0.0f;
  1984. }
  1985. pOutSoundInfo->nNumEventParameters = pEntry->EventParameters.size();
  1986. }
  1987. else
  1988. {
  1989. // not enough memory, say what is needed
  1990. pOutSoundInfo->nNumEventParameters = pEntry->EventParameters.size();
  1991. bResult = (pOutSoundInfo->pSoundMoods == NULL); // only report error if memory was provided but is too small
  1992. }
  1993. }
  1994. }
  1995. return bResult;
  1996. }
  1997. //////////////////////////////////////////////////////////////////////////
  1998. int CLocalizedStringsManager::GetLocalizedStringCount()
  1999. {
  2000. if (!m_pLanguage)
  2001. {
  2002. return 0;
  2003. }
  2004. return static_cast<int>(m_pLanguage->m_vLocalizedStrings.size());
  2005. }
  2006. //////////////////////////////////////////////////////////////////////////
  2007. bool CLocalizedStringsManager::GetLocalizedInfoByIndex(int nIndex, SLocalizedInfoGame& outGameInfo)
  2008. {
  2009. if (!m_pLanguage)
  2010. {
  2011. return false;
  2012. }
  2013. const std::vector<SLocalizedStringEntry*>& entryVec = m_pLanguage->m_vLocalizedStrings;
  2014. if (nIndex < 0 || nIndex >= (int)entryVec.size())
  2015. {
  2016. return false;
  2017. }
  2018. const SLocalizedStringEntry* pEntry = entryVec[nIndex];
  2019. outGameInfo.szCharacterName = pEntry->sCharacterName.c_str();
  2020. outGameInfo.sUtf8TranslatedText = pEntry->GetTranslatedText(m_pLanguage);
  2021. outGameInfo.bUseSubtitle = (pEntry->flags & SLocalizedStringEntry::USE_SUBTITLE);
  2022. return true;
  2023. }
  2024. //////////////////////////////////////////////////////////////////////////
  2025. bool CLocalizedStringsManager::GetLocalizedInfoByIndex(int nIndex, SLocalizedInfoEditor& outEditorInfo)
  2026. {
  2027. if (!m_pLanguage)
  2028. {
  2029. return false;
  2030. }
  2031. const std::vector<SLocalizedStringEntry*>& entryVec = m_pLanguage->m_vLocalizedStrings;
  2032. if (nIndex < 0 || nIndex >= (int)entryVec.size())
  2033. {
  2034. return false;
  2035. }
  2036. const SLocalizedStringEntry* pEntry = entryVec[nIndex];
  2037. outEditorInfo.szCharacterName = pEntry->sCharacterName.c_str();
  2038. outEditorInfo.sUtf8TranslatedText = pEntry->GetTranslatedText(m_pLanguage);
  2039. assert(pEntry->pEditorExtension != NULL);
  2040. outEditorInfo.sKey = pEntry->pEditorExtension->sKey.c_str();
  2041. outEditorInfo.sOriginalActorLine = pEntry->pEditorExtension->sOriginalActorLine.c_str();
  2042. outEditorInfo.sUtf8TranslatedActorLine = pEntry->pEditorExtension->sUtf8TranslatedActorLine.c_str();
  2043. //outEditorInfo.sOriginalText = pEntry->sOriginalText;
  2044. outEditorInfo.sOriginalCharacterName = pEntry->pEditorExtension->sOriginalCharacterName.c_str();
  2045. outEditorInfo.nRow = pEntry->pEditorExtension->nRow;
  2046. outEditorInfo.bUseSubtitle = (pEntry->flags & SLocalizedStringEntry::USE_SUBTITLE);
  2047. return true;
  2048. }
  2049. //////////////////////////////////////////////////////////////////////////
  2050. bool CLocalizedStringsManager::GetSubtitle(const char* sKeyOrLabel, AZStd::string& outSubtitle, bool bForceSubtitle)
  2051. {
  2052. assert(sKeyOrLabel);
  2053. if (!m_pLanguage || !sKeyOrLabel || !*sKeyOrLabel)
  2054. {
  2055. return false;
  2056. }
  2057. if (*sKeyOrLabel == '@')
  2058. {
  2059. ++sKeyOrLabel;
  2060. }
  2061. uint32 keyCRC32 = AZ::Crc32(sKeyOrLabel);
  2062. {
  2063. AutoLock lock(m_cs); //Lock here, to prevent strings etc being modified underneath this lookup
  2064. const SLocalizedStringEntry* pEntry = stl::find_in_map(m_pLanguage->m_keysMap, keyCRC32, NULL);
  2065. if (pEntry != NULL)
  2066. {
  2067. if ((pEntry->flags & SLocalizedStringEntry::USE_SUBTITLE) == false && !bForceSubtitle)
  2068. {
  2069. return false;
  2070. }
  2071. // TODO verify that no fallback is needed
  2072. outSubtitle = pEntry->GetTranslatedText(m_pLanguage);
  2073. if (outSubtitle.empty() == true)
  2074. {
  2075. if (pEntry->pEditorExtension != NULL && pEntry->pEditorExtension->sUtf8TranslatedActorLine.empty() == false)
  2076. {
  2077. outSubtitle = pEntry->pEditorExtension->sUtf8TranslatedActorLine;
  2078. }
  2079. else if (pEntry->pEditorExtension != NULL && pEntry->pEditorExtension->sOriginalText.empty() == false)
  2080. {
  2081. outSubtitle = pEntry->pEditorExtension->sOriginalText;
  2082. }
  2083. else if (pEntry->pEditorExtension != NULL && pEntry->pEditorExtension->sOriginalActorLine.empty() == false)
  2084. {
  2085. outSubtitle = pEntry->pEditorExtension->sOriginalActorLine;
  2086. }
  2087. }
  2088. return true;
  2089. }
  2090. return false;
  2091. }
  2092. }
  2093. void InternalFormatStringMessage(AZStd::string& outString, const AZStd::string& sString, const char** sParams, int nParams)
  2094. {
  2095. static const char token = '%';
  2096. static const char tokens1[2] = { token, '\0' };
  2097. static const char tokens2[3] = { token, token, '\0' };
  2098. int maxArgUsed = 0;
  2099. size_t lastPos = 0;
  2100. size_t curPos = 0;
  2101. const int sourceLen = static_cast<int>(sString.length());
  2102. while (true)
  2103. {
  2104. auto foundPos = sString.find(token, curPos);
  2105. if (foundPos != AZStd::string::npos)
  2106. {
  2107. if (foundPos + 1 < sourceLen)
  2108. {
  2109. const int nArg = sString[foundPos + 1] - '1';
  2110. if (nArg >= 0 && nArg <= 9)
  2111. {
  2112. if (nArg < nParams)
  2113. {
  2114. outString.append(sString, lastPos, foundPos - lastPos);
  2115. outString.append(sParams[nArg]);
  2116. curPos = foundPos + 2;
  2117. lastPos = curPos;
  2118. maxArgUsed = std::max(maxArgUsed, nArg);
  2119. }
  2120. else
  2121. {
  2122. AZStd::string tmp(sString);
  2123. AZ::StringFunc::Replace(tmp, tokens1, tokens2);
  2124. if constexpr (sizeof(*tmp.c_str()) == sizeof(char))
  2125. {
  2126. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Parameter for argument %d is missing. [%s]", nArg + 1, (const char*)tmp.c_str());
  2127. }
  2128. else
  2129. {
  2130. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Parameter for argument %d is missing. [%S]", nArg + 1, (const wchar_t*)tmp.c_str());
  2131. }
  2132. curPos = foundPos + 1;
  2133. }
  2134. }
  2135. else
  2136. {
  2137. curPos = foundPos + 1;
  2138. }
  2139. }
  2140. else
  2141. {
  2142. curPos = foundPos + 1;
  2143. }
  2144. }
  2145. else
  2146. {
  2147. outString.append(sString, lastPos, sourceLen - lastPos);
  2148. break;
  2149. }
  2150. }
  2151. }
  2152. void InternalFormatStringMessage(AZStd::string& outString, const AZStd::string& sString, const char* param1, const char* param2 = 0, const char* param3 = 0, const char* param4 = 0)
  2153. {
  2154. static const int MAX_PARAMS = 4;
  2155. const char* params[MAX_PARAMS] = { param1, param2, param3, param4 };
  2156. int nParams = 0;
  2157. while (nParams < MAX_PARAMS && params[nParams])
  2158. {
  2159. ++nParams;
  2160. }
  2161. InternalFormatStringMessage(outString, sString, params, nParams);
  2162. }
  2163. //////////////////////////////////////////////////////////////////////////
  2164. void CLocalizedStringsManager::FormatStringMessage_List(AZStd::string& outString, const AZStd::string& sString, const char** sParams, int nParams)
  2165. {
  2166. InternalFormatStringMessage(outString, sString, sParams, nParams);
  2167. }
  2168. //////////////////////////////////////////////////////////////////////////
  2169. void CLocalizedStringsManager::FormatStringMessage(AZStd::string& outString, const AZStd::string& sString, const char* param1, const char* param2, const char* param3, const char* param4)
  2170. {
  2171. InternalFormatStringMessage(outString, sString, param1, param2, param3, param4);
  2172. }
  2173. //////////////////////////////////////////////////////////////////////////
  2174. #if defined (WIN32) || defined(WIN64)
  2175. namespace
  2176. {
  2177. struct LanguageID
  2178. {
  2179. const char* language;
  2180. unsigned long lcID;
  2181. };
  2182. LanguageID languageIDArray[] =
  2183. {
  2184. { "en-US", 0x0409 }, // English (USA)
  2185. { "en-GB", 0x0809 }, // English (UK)
  2186. { "de-DE", 0x0407 }, // German
  2187. { "ru-RU", 0x0419 }, // Russian (Russia)
  2188. { "pl-PL", 0x0415 }, // Polish
  2189. { "tr-TR", 0x041f }, // Turkish
  2190. { "es-ES", 0x0c0a }, // Spanish (Spain)
  2191. { "es-MX", 0x080a }, // Spanish (Mexico)
  2192. { "fr-FR", 0x040c }, // French (France)
  2193. { "fr-CA", 0x0c0c }, // French (Canada)
  2194. { "it-IT", 0x0410 }, // Italian
  2195. { "pt-PT", 0x0816 }, // Portugese (Portugal)
  2196. { "pt-BR", 0x0416 }, // Portugese (Brazil)
  2197. { "ja-JP", 0x0411 }, // Japanese
  2198. { "ko-KR", 0x0412 }, // Korean
  2199. { "zh-CHT", 0x0804 }, // Traditional Chinese
  2200. { "zh-CHS", 0x0804 }, // Simplified Chinese
  2201. { "nl-NL", 0x0413 }, // Dutch (The Netherlands)
  2202. { "fi-FI", 0x040b }, // Finnish
  2203. { "sv-SE", 0x041d }, // Swedish
  2204. { "cs-CZ", 0x0405 }, // Czech
  2205. { "no-NO", 0x0414 }, // Norwegian (Norway)
  2206. { "ar-SA", 0x0401 }, // Arabic (Saudi Arabia)
  2207. { "da-DK", 0x0406 }, // Danish (Denmark)
  2208. };
  2209. const size_t numLanguagesIDs = sizeof(languageIDArray) / sizeof(languageIDArray[0]);
  2210. LanguageID GetLanguageID(const char* language)
  2211. {
  2212. // default is English (US)
  2213. const LanguageID defaultLanguage = { "en-US", 0x0409 };
  2214. for (int i = 0; i < numLanguagesIDs; ++i)
  2215. {
  2216. if (_stricmp(language, languageIDArray[i].language) == 0)
  2217. {
  2218. return languageIDArray[i];
  2219. }
  2220. }
  2221. return defaultLanguage;
  2222. }
  2223. LanguageID g_currentLanguageID = { 0, 0 };
  2224. };
  2225. #endif
  2226. void CLocalizedStringsManager::InternalSetCurrentLanguage(CLocalizedStringsManager::SLanguage* pLanguage)
  2227. {
  2228. m_pLanguage = pLanguage;
  2229. #if defined (WIN32) || defined(WIN64)
  2230. if (m_pLanguage != 0)
  2231. {
  2232. g_currentLanguageID = GetLanguageID(m_pLanguage->sLanguage.c_str());
  2233. }
  2234. else
  2235. {
  2236. g_currentLanguageID.language = 0;
  2237. g_currentLanguageID.lcID = 0;
  2238. }
  2239. #endif
  2240. // TODO: on Linux based systems we should now set the locale
  2241. // Enabling this on windows something seems to get corrupted...
  2242. // on Windows we always use Windows Platform Functions, which use the lcid
  2243. #if 0
  2244. if (g_currentLanguageID.language)
  2245. {
  2246. const char* currentLocale = setlocale(LC_ALL, 0);
  2247. if (0 == setlocale(LC_ALL, g_currentLanguageID.language))
  2248. {
  2249. setlocale(LC_ALL, currentLocale);
  2250. }
  2251. }
  2252. else
  2253. {
  2254. setlocale(LC_ALL, "C");
  2255. }
  2256. #endif
  2257. ReloadData();
  2258. if (gEnv->pCryFont)
  2259. {
  2260. gEnv->pCryFont->OnLanguageChanged();
  2261. }
  2262. }
  2263. void CLocalizedStringsManager::LocalizeDuration(int seconds, AZStd::string& outDurationString)
  2264. {
  2265. int s = seconds;
  2266. int d, h, m;
  2267. d = s / 86400;
  2268. s -= d * 86400;
  2269. h = s / 3600;
  2270. s -= h * 3600;
  2271. m = s / 60;
  2272. s = s - m * 60;
  2273. AZStd::string str;
  2274. if (d > 1)
  2275. {
  2276. str = AZStd::string::format("%d @ui_days %02d:%02d:%02d", d, h, m, s);
  2277. }
  2278. else if (d > 0)
  2279. {
  2280. str = AZStd::string::format("%d @ui_day %02d:%02d:%02d", d, h, m, s);
  2281. }
  2282. else if (h > 0)
  2283. {
  2284. str = AZStd::string::format("%02d:%02d:%02d", h, m, s);
  2285. }
  2286. else
  2287. {
  2288. str = AZStd::string::format("%02d:%02d", m, s);
  2289. }
  2290. LocalizeString_s(str, outDurationString);
  2291. }
  2292. void CLocalizedStringsManager::LocalizeNumber(int number, AZStd::string& outNumberString)
  2293. {
  2294. if (number == 0)
  2295. {
  2296. outNumberString.assign("0");
  2297. return;
  2298. }
  2299. outNumberString.assign("");
  2300. int n = abs(number);
  2301. AZStd::string separator;
  2302. AZStd::fixed_string<64> tmp;
  2303. LocalizeString_ch("@ui_thousand_separator", separator);
  2304. while (n > 0)
  2305. {
  2306. int a = n / 1000;
  2307. int b = n - (a * 1000);
  2308. if (a > 0)
  2309. {
  2310. tmp = AZStd::string::format("%s%03d%s", separator.c_str(), b, tmp.c_str());
  2311. }
  2312. else
  2313. {
  2314. tmp = AZStd::string::format("%d%s", b, tmp.c_str());
  2315. }
  2316. n = a;
  2317. }
  2318. if (number < 0)
  2319. {
  2320. tmp = AZStd::string::format("-%s", tmp.c_str());
  2321. }
  2322. outNumberString.assign(tmp.c_str());
  2323. }
  2324. void CLocalizedStringsManager::LocalizeNumber_Decimal(float number, int decimals, AZStd::string& outNumberString)
  2325. {
  2326. if (number == 0.0f)
  2327. {
  2328. AZStd::fixed_string<64> tmp;
  2329. tmp = AZStd::fixed_string<64>::format("%.*f", decimals, number);
  2330. outNumberString.assign(tmp.c_str());
  2331. return;
  2332. }
  2333. outNumberString.assign("");
  2334. AZStd::string commaSeparator;
  2335. LocalizeString_ch("@ui_decimal_separator", commaSeparator);
  2336. float f = number > 0.0f ? number : -number;
  2337. int d = (int)f;
  2338. AZStd::string intPart;
  2339. LocalizeNumber(d, intPart);
  2340. float decimalsOnly = f - (float)d;
  2341. int decimalsAsInt = aznumeric_cast<int>(int_round(decimalsOnly * pow(10.0f, decimals)));
  2342. AZStd::fixed_string<64> tmp;
  2343. tmp = AZStd::fixed_string<64>::format("%s%s%0*d", intPart.c_str(), commaSeparator.c_str(), decimals, decimalsAsInt);
  2344. outNumberString.assign(tmp.c_str());
  2345. }
  2346. bool CLocalizedStringsManager::ProjectUsesLocalization() const
  2347. {
  2348. ICVar* pCVar = gEnv->pConsole->GetCVar("sys_localization_folder");
  2349. CRY_ASSERT_MESSAGE(pCVar,
  2350. "Console variable 'sys_localization_folder' not defined! "
  2351. "This was previously defined at startup in CSystem::CreateSystemVars.");
  2352. // If game.cfg didn't provide a sys_localization_folder, we'll assume that
  2353. // the project doesn't want to use localization features.
  2354. return (pCVar->GetFlags() & VF_WASINCONFIG) != 0;
  2355. }
  2356. #if defined (WIN32) || defined(WIN64)
  2357. namespace
  2358. {
  2359. void UnixTimeToFileTime(time_t unixtime, FILETIME* filetime)
  2360. {
  2361. LONGLONG longlong = Int32x32To64(unixtime, 10000000) + 116444736000000000;
  2362. filetime->dwLowDateTime = (DWORD) longlong;
  2363. filetime->dwHighDateTime = (DWORD)(longlong >> 32);
  2364. }
  2365. void UnixTimeToSystemTime(time_t unixtime, SYSTEMTIME* systemtime)
  2366. {
  2367. FILETIME filetime;
  2368. UnixTimeToFileTime(unixtime, &filetime);
  2369. FileTimeToSystemTime(&filetime, systemtime);
  2370. }
  2371. }
  2372. void CLocalizedStringsManager::LocalizeTime(time_t t, bool bMakeLocalTime, bool bShowSeconds, AZStd::string& outTimeString)
  2373. {
  2374. if (bMakeLocalTime)
  2375. {
  2376. struct tm thetime;
  2377. localtime_s(&thetime, &t);
  2378. t = DateToSecondsUTC(thetime);
  2379. }
  2380. outTimeString.clear();
  2381. LCID lcID = g_currentLanguageID.lcID ? g_currentLanguageID.lcID : LOCALE_USER_DEFAULT;
  2382. DWORD flags = bShowSeconds == false ? TIME_NOSECONDS : 0;
  2383. SYSTEMTIME systemTime;
  2384. UnixTimeToSystemTime(t, &systemTime);
  2385. int len = ::GetTimeFormatW(lcID, flags, &systemTime, 0, 0, 0);
  2386. if (len > 0)
  2387. {
  2388. // len includes terminating null!
  2389. AZStd::fixed_wstring<256> tmpString;
  2390. tmpString.resize(len);
  2391. ::GetTimeFormatW(lcID, flags, &systemTime, 0, (wchar_t*) tmpString.c_str(), len);
  2392. AZStd::to_string(outTimeString, tmpString.data());
  2393. }
  2394. }
  2395. void CLocalizedStringsManager::LocalizeDate(time_t t, bool bMakeLocalTime, bool bShort, bool bIncludeWeekday, AZStd::string& outDateString)
  2396. {
  2397. if (bMakeLocalTime)
  2398. {
  2399. struct tm thetime;
  2400. localtime_s(&thetime, &t);
  2401. t = DateToSecondsUTC(thetime);
  2402. }
  2403. outDateString.resize(0);
  2404. LCID lcID = g_currentLanguageID.lcID ? g_currentLanguageID.lcID : LOCALE_USER_DEFAULT;
  2405. SYSTEMTIME systemTime;
  2406. UnixTimeToSystemTime(t, &systemTime);
  2407. // len includes terminating null!
  2408. AZStd::fixed_wstring<256> tmpString;
  2409. if (bIncludeWeekday)
  2410. {
  2411. // Get name of day
  2412. int len = ::GetDateFormatW(lcID, 0, &systemTime, L"ddd", 0, 0);
  2413. if (len > 0)
  2414. {
  2415. // len includes terminating null!
  2416. tmpString.resize(len);
  2417. ::GetDateFormatW(lcID, 0, &systemTime, L"ddd", (wchar_t*) tmpString.c_str(), len);
  2418. AZStd::string utf8;
  2419. AZStd::to_string(utf8, tmpString.data());
  2420. outDateString.append(utf8);
  2421. outDateString.append(" ");
  2422. }
  2423. }
  2424. DWORD flags = bShort ? DATE_SHORTDATE : DATE_LONGDATE;
  2425. int len = ::GetDateFormatW(lcID, flags, &systemTime, 0, 0, 0);
  2426. if (len > 0)
  2427. {
  2428. // len includes terminating null!
  2429. tmpString.resize(len);
  2430. ::GetDateFormatW(lcID, flags, &systemTime, 0, (wchar_t*) tmpString.c_str(), len);
  2431. AZStd::string utf8;
  2432. AZStd::to_string(utf8, tmpString.data());
  2433. outDateString.append(utf8);
  2434. }
  2435. }
  2436. #else // #if defined (WIN32) || defined(WIN64)
  2437. void CLocalizedStringsManager::LocalizeTime(time_t t, bool bMakeLocalTime, bool bShowSeconds, AZStd::string& outTimeString)
  2438. {
  2439. struct tm theTime;
  2440. if (bMakeLocalTime)
  2441. {
  2442. #if AZ_TRAIT_USE_SECURE_CRT_FUNCTIONS
  2443. localtime_s(&theTime, &t);
  2444. #else
  2445. theTime = *localtime(&t);
  2446. #endif
  2447. }
  2448. else
  2449. {
  2450. #if AZ_TRAIT_USE_SECURE_CRT_FUNCTIONS
  2451. gmtime_s(&theTime, &t);
  2452. #else
  2453. theTime = *gmtime(&t);
  2454. #endif
  2455. }
  2456. wchar_t buf[256];
  2457. const size_t bufSize = sizeof(buf) / sizeof(buf[0]);
  2458. wcsftime(buf, bufSize, bShowSeconds ? L"%#X" : L"%X", &theTime);
  2459. buf[bufSize - 1] = 0;
  2460. AZStd::to_string(outTimeString, buf);
  2461. }
  2462. void CLocalizedStringsManager::LocalizeDate(time_t t, bool bMakeLocalTime, bool bShort, bool bIncludeWeekday, AZStd::string& outDateString)
  2463. {
  2464. struct tm theTime;
  2465. if (bMakeLocalTime)
  2466. {
  2467. #if AZ_TRAIT_USE_SECURE_CRT_FUNCTIONS
  2468. localtime_s(&theTime, &t);
  2469. #else
  2470. theTime = *localtime(&t);
  2471. #endif
  2472. }
  2473. else
  2474. {
  2475. #if AZ_TRAIT_USE_SECURE_CRT_FUNCTIONS
  2476. gmtime_s(&theTime, &t);
  2477. #else
  2478. theTime = *gmtime(&t);
  2479. #endif
  2480. }
  2481. wchar_t buf[256];
  2482. const size_t bufSize = sizeof(buf) / sizeof(buf[0]);
  2483. const wchar_t* format = bShort ? (bIncludeWeekday ? L"%a %x" : L"%x") : L"%#x"; // long format always contains Weekday name
  2484. wcsftime(buf, bufSize, format, &theTime);
  2485. buf[bufSize - 1] = 0;
  2486. AZStd::to_string(outDateString, buf);
  2487. }
  2488. #endif