From ff58784229cd124a9ca64d958b9b75a5697622a9 Mon Sep 17 00:00:00 2001 From: SimplestStudio Date: Wed, 18 Jun 2025 11:28:57 +0300 Subject: [PATCH 1/7] [win-linux] for bug 68388: detect color printing support --- win-linux/src/cprintdata.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/win-linux/src/cprintdata.cpp b/win-linux/src/cprintdata.cpp index f4600a6ea..6267a2532 100644 --- a/win-linux/src/cprintdata.cpp +++ b/win-linux/src/cprintdata.cpp @@ -188,10 +188,12 @@ public: PRINTER_INFO_4 *printers = reinterpret_cast(buf.data()); for (DWORD i = 0; i < ret; ++i) { bool duplex_supported = (DeviceCapabilities(printers[i].pPrinterName, NULL, DC_DUPLEX, NULL, NULL) == 1); + bool color_supported = (DeviceCapabilities(printers[i].pPrinterName, NULL, DC_COLORDEVICE, NULL, NULL) == 1); QJsonObject printerObject; printerObject["name"] = QString::fromWCharArray(printers[i].pPrinterName); printerObject["duplex_supported"] = duplex_supported; + printerObject["color_supported"] = color_supported; constexpr int PAPER_NAME_LENGTH = 64; bool paperNamesSuccess = false, paperSizeSuccess = false; @@ -243,9 +245,18 @@ public: QJsonObject printerObject; printerObject["name"] = QString::fromUtf8(dest->name); printerObject["duplex_supported"] = duplex_supported; + printerObject["color_supported"] = false; ppd_option_t *option = ppdFirstOption(ppdF); while (option) { + if (strcmp(option->keyword, "ColorModel") == 0) { + for (int j = 0; j < option->num_choices; j++) { + if (strcmp(option->choices[j].choice, "Gray") != 0) { + printerObject["color_supported"] = true; + break; + } + } + } else if (strcmp(option->keyword, "PageSize") == 0) { QJsonArray paperArray; for (int j = 0; j < option->num_choices; j++) { From 9734ceaf6e09fc3cc2027c5a14ac744e29db52cc Mon Sep 17 00:00:00 2001 From: SimplestStudio Date: Wed, 18 Jun 2025 17:12:09 +0300 Subject: [PATCH 2/7] [win-linux] updatesvc: fix translation format --- win-linux/extras/update-daemon/UpdateDaemon.pro | 4 ++-- .../extras/update-daemon/res/langs/{langs.iss => langs.isl} | 0 win-linux/extras/update-daemon/src/platform_linux/main.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename win-linux/extras/update-daemon/res/langs/{langs.iss => langs.isl} (100%) diff --git a/win-linux/extras/update-daemon/UpdateDaemon.pro b/win-linux/extras/update-daemon/UpdateDaemon.pro index a9eead74f..7eff9c68b 100644 --- a/win-linux/extras/update-daemon/UpdateDaemon.pro +++ b/win-linux/extras/update-daemon/UpdateDaemon.pro @@ -4,7 +4,7 @@ include(common.pri) DEFINES += COPYRIGHT_YEAR=$${CURRENT_YEAR} DEFINES += APP_ICON_PATH=\"./icons/desktopeditors.ico\" -DEFINES += APP_LANG_PATH=\"./langs/langs.iss\" +DEFINES += APP_LANG_PATH=\"./langs/langs.isl\" ENV_URL_APPCAST_MAIN = $$(DESKTOP_URL_UPDATES_MAIN_CHANNEL) !isEmpty(ENV_URL_APPCAST_MAIN) { @@ -30,4 +30,4 @@ core_linux { QMAKE_EXTRA_COMPILERS += glib_resources } -OTHER_FILES += $$PWD/res/langs/langs.iss +OTHER_FILES += $$PWD/res/langs/langs.isl diff --git a/win-linux/extras/update-daemon/res/langs/langs.iss b/win-linux/extras/update-daemon/res/langs/langs.isl similarity index 100% rename from win-linux/extras/update-daemon/res/langs/langs.iss rename to win-linux/extras/update-daemon/res/langs/langs.isl diff --git a/win-linux/extras/update-daemon/src/platform_linux/main.cpp b/win-linux/extras/update-daemon/src/platform_linux/main.cpp index 98f9dd10e..8a556ac7a 100644 --- a/win-linux/extras/update-daemon/src/platform_linux/main.cpp +++ b/win-linux/extras/update-daemon/src/platform_linux/main.cpp @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) if (NS_Utils::cmdArgContains("--log")) NS_Logger::AllowWriteLog(); std::locale::global(std::locale("")); - Translator lang(NS_Utils::GetAppLanguage(), "/langs/langs.iss"); + Translator lang(NS_Utils::GetAppLanguage(), "/langs/langs.isl"); CSocket socket(0, INSTANCE_SVC_PORT); if (!socket.isPrimaryInstance()) return 0; From 0cf7a84ad45b17738155e3ba9ddc70c17c7511e4 Mon Sep 17 00:00:00 2001 From: SimplestStudio Date: Wed, 18 Jun 2025 17:16:33 +0300 Subject: [PATCH 3/7] [win-linux] updatesvc: translator refactoring --- .../update-daemon/src/classes/translator.cpp | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/win-linux/extras/update-daemon/src/classes/translator.cpp b/win-linux/extras/update-daemon/src/classes/translator.cpp index 83bafcdbc..35d748ef3 100644 --- a/win-linux/extras/update-daemon/src/classes/translator.cpp +++ b/win-linux/extras/update-daemon/src/classes/translator.cpp @@ -132,28 +132,21 @@ tstring Translator::tr(const char *str) tstring translatedStr = str; #endif if (is_translations_valid) { - for (auto &strIdPair : translMap) { - //LocaleMap locMap = strIdPair.second; - // for (LocaleMap::const_iterator it = strIdPair.second.begin(); it != strIdPair.second.end(); ++it) { - //wcout << L"\n\n" << translatedStr << L"\n" << it->second; - if (strIdPair.first == translatedStr) { - if (strIdPair.second.find(langName) != strIdPair.second.end()) - return strIdPair.second[langName]; - else { - tstring primaryLangAndScript = getPrimaryLang(langName, true); - if (strIdPair.second.find(primaryLangAndScript) != strIdPair.second.end()) - return strIdPair.second[primaryLangAndScript]; - else { - tstring primaryLang = getPrimaryLang(langName); - if (strIdPair.second.find(primaryLang) != strIdPair.second.end()) - return strIdPair.second[primaryLang]; - } + auto it = translMap.find(translatedStr); + if (it != translMap.end()) { + LocaleMap &lcmap = it->second; + auto lc_it = lcmap.find(langName); + if (lc_it == lcmap.end()) { + tstring primaryLangAndScript = getPrimaryLang(langName, true); + if ((lc_it = lcmap.find(primaryLangAndScript)) == lcmap.end()) { + tstring primaryLang = getPrimaryLang(langName); + if ((lc_it = lcmap.find(primaryLang)) == lcmap.end()) { + lc_it = lcmap.find(_T("en")); } - if (strIdPair.second.find(_T("en")) != strIdPair.second.end()) - return strIdPair.second[_T("en")]; - break; } - // } + } + if (lc_it != lcmap.end()) + return lc_it->second; } } return translatedStr; From b121ba6c05c89d3548359e71069232510801230f Mon Sep 17 00:00:00 2001 From: SimplestStudio Date: Wed, 18 Jun 2025 18:24:57 +0300 Subject: [PATCH 4/7] [win-linux] updatesvc: use singleton access for Translator --- .../update-daemon/src/classes/csvcmanager.cpp | 2 +- .../update-daemon/src/classes/translator.cpp | 19 ++++++++---- .../update-daemon/src/classes/translator.h | 29 +++++++++++-------- .../update-daemon/src/platform_linux/main.cpp | 2 +- .../update-daemon/src/platform_win/main.cpp | 4 +-- 5 files changed, 35 insertions(+), 21 deletions(-) diff --git a/win-linux/extras/update-daemon/src/classes/csvcmanager.cpp b/win-linux/extras/update-daemon/src/classes/csvcmanager.cpp index 8c9632f02..de9e7aefa 100644 --- a/win-linux/extras/update-daemon/src/classes/csvcmanager.cpp +++ b/win-linux/extras/update-daemon/src/classes/csvcmanager.cpp @@ -381,7 +381,7 @@ void CSvcManager::init() break; case MSG_SetLanguage: - Translator::setLanguage(params[1]); + Translator::instance().setLanguage(params[1]); break; default: diff --git a/win-linux/extras/update-daemon/src/classes/translator.cpp b/win-linux/extras/update-daemon/src/classes/translator.cpp index 35d748ef3..ac7ec674b 100644 --- a/win-linux/extras/update-daemon/src/classes/translator.cpp +++ b/win-linux/extras/update-daemon/src/classes/translator.cpp @@ -61,14 +61,23 @@ wstring StrToWStr(const string &str) } #endif -TranslationsMap Translator::translMap = TranslationsMap(); -tstring Translator::langName = _T("en"); -bool Translator::is_translations_valid = false; +Translator::Translator() : + langName(_T("en")), + is_translations_valid(false) +{ + +} + +Translator& Translator::instance() +{ + static Translator inst; + return inst; +} #ifdef _WIN32 -Translator::Translator(const tstring &lang, int resourceId) +void Translator::init(const tstring &lang, int resourceId) #else -Translator::Translator(const tstring &lang, const char *resourcePath) +void Translator::init(const tstring &lang, const char *resourcePath) #endif { langName = lang; diff --git a/win-linux/extras/update-daemon/src/classes/translator.h b/win-linux/extras/update-daemon/src/classes/translator.h index 28bb5f680..9594e3fa6 100644 --- a/win-linux/extras/update-daemon/src/classes/translator.h +++ b/win-linux/extras/update-daemon/src/classes/translator.h @@ -13,7 +13,7 @@ # define tstring std::string #endif -#define _TR(str) Translator::tr(#str) +#define _TR(str) Translator::instance().tr(#str) using std::unordered_map; @@ -24,24 +24,29 @@ typedef unordered_map TranslationsMap; class Translator { public: -#ifdef _WIN32 - Translator(const tstring &lang, int resourceId); -#else - Translator(const tstring &lang, const char *resourcePath); -#endif - ~Translator(); + Translator(const Translator&) = delete; + Translator& operator=(const Translator&) = delete; + static Translator& instance(); - static tstring tr(const char*); - static void setLanguage(const tstring &lang); +#ifdef _WIN32 + void init(const tstring &lang, int resourceId); +#else + void init(const tstring &lang, const char *resourcePath); +#endif + tstring tr(const char*); + void setLanguage(const tstring &lang); private: + Translator(); + ~Translator(); + void parseTranslations(); - static TranslationsMap translMap; + TranslationsMap translMap; tstring translations, error_substr; - static tstring langName; - static bool is_translations_valid; + tstring langName; + bool is_translations_valid; enum TokenType { TOKEN_BEGIN_DOCUMENT = 0, diff --git a/win-linux/extras/update-daemon/src/platform_linux/main.cpp b/win-linux/extras/update-daemon/src/platform_linux/main.cpp index 8a556ac7a..e730ed608 100644 --- a/win-linux/extras/update-daemon/src/platform_linux/main.cpp +++ b/win-linux/extras/update-daemon/src/platform_linux/main.cpp @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) if (NS_Utils::cmdArgContains("--log")) NS_Logger::AllowWriteLog(); std::locale::global(std::locale("")); - Translator lang(NS_Utils::GetAppLanguage(), "/langs/langs.isl"); + Translator::instance().init(NS_Utils::GetAppLanguage(), "/langs/langs.isl"); CSocket socket(0, INSTANCE_SVC_PORT); if (!socket.isPrimaryInstance()) return 0; diff --git a/win-linux/extras/update-daemon/src/platform_win/main.cpp b/win-linux/extras/update-daemon/src/platform_win/main.cpp index ab06c28bc..a626adb99 100644 --- a/win-linux/extras/update-daemon/src/platform_win/main.cpp +++ b/win-linux/extras/update-daemon/src/platform_win/main.cpp @@ -102,7 +102,7 @@ int __cdecl _tmain (int argc, TCHAR *argv[]) if (NS_Utils::cmdArgContains(_T("--log"))) NS_Logger::AllowWriteLog(); std::locale::global(std::locale("")); - Translator lang(NS_Utils::GetAppLanguage().c_str(), IDT_TRANSLATIONS); + Translator::instance().init(NS_Utils::GetAppLanguage().c_str(), IDT_TRANSLATIONS); CSocket socket(0, INSTANCE_SVC_PORT); if (!socket.isPrimaryInstance()) return 0; @@ -144,7 +144,7 @@ int __cdecl _tmain (int argc, TCHAR *argv[]) } std::locale::global(std::locale("")); - Translator lang(NS_Utils::GetAppLanguage().c_str(), IDT_TRANSLATIONS); + Translator::instance().init(NS_Utils::GetAppLanguage().c_str(), IDT_TRANSLATIONS); SERVICE_TABLE_ENTRY DispatchTable[] = { {(LPTSTR)SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)SvcMain}, From af416f6e589b5f8f3d5a0eef016ed9f26d9d939d Mon Sep 17 00:00:00 2001 From: SimplestStudio Date: Wed, 18 Jun 2025 18:25:49 +0300 Subject: [PATCH 5/7] [win-linux] updatesvc: refactoring func Translator::tr --- .../extras/update-daemon/src/classes/translator.cpp | 13 ++++--------- .../extras/update-daemon/src/classes/translator.h | 4 ++-- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/win-linux/extras/update-daemon/src/classes/translator.cpp b/win-linux/extras/update-daemon/src/classes/translator.cpp index ac7ec674b..cd3bf4414 100644 --- a/win-linux/extras/update-daemon/src/classes/translator.cpp +++ b/win-linux/extras/update-daemon/src/classes/translator.cpp @@ -133,17 +133,12 @@ Translator::~Translator() } -tstring Translator::tr(const char *str) +tstring Translator::tr(const tchar *str) const { -#ifdef _WIN32 - tstring translatedStr = StrToWStr(str); -#else - tstring translatedStr = str; -#endif if (is_translations_valid) { - auto it = translMap.find(translatedStr); + auto it = translMap.find(str); if (it != translMap.end()) { - LocaleMap &lcmap = it->second; + const LocaleMap &lcmap = it->second; auto lc_it = lcmap.find(langName); if (lc_it == lcmap.end()) { tstring primaryLangAndScript = getPrimaryLang(langName, true); @@ -158,7 +153,7 @@ tstring Translator::tr(const char *str) return lc_it->second; } } - return translatedStr; + return str; } void Translator::setLanguage(const tstring &lang) diff --git a/win-linux/extras/update-daemon/src/classes/translator.h b/win-linux/extras/update-daemon/src/classes/translator.h index 9594e3fa6..2492beb17 100644 --- a/win-linux/extras/update-daemon/src/classes/translator.h +++ b/win-linux/extras/update-daemon/src/classes/translator.h @@ -13,7 +13,7 @@ # define tstring std::string #endif -#define _TR(str) Translator::instance().tr(#str) +#define _TR(str) Translator::instance().tr(_T(#str)) using std::unordered_map; @@ -33,7 +33,7 @@ public: #else void init(const tstring &lang, const char *resourcePath); #endif - tstring tr(const char*); + tstring tr(const tchar*) const; void setLanguage(const tstring &lang); private: From e0c38273b4b716acc0ebe31c53759a23af899301 Mon Sep 17 00:00:00 2001 From: SimplestStudio Date: Wed, 18 Jun 2025 22:47:37 +0300 Subject: [PATCH 6/7] [win-linux] updatesvc: switch Translator to bin format --- .../extras/update-daemon/UpdateDaemon.pro | 2 +- .../extras/update-daemon/res/gresource.xml | 2 +- .../extras/update-daemon/res/langs/langs.bin | Bin 0 -> 49331 bytes .../update-daemon/src/classes/translator.cpp | 390 +++++++++--------- .../update-daemon/src/classes/translator.h | 4 - .../update-daemon/src/platform_linux/main.cpp | 2 +- 6 files changed, 190 insertions(+), 210 deletions(-) create mode 100644 win-linux/extras/update-daemon/res/langs/langs.bin diff --git a/win-linux/extras/update-daemon/UpdateDaemon.pro b/win-linux/extras/update-daemon/UpdateDaemon.pro index 7eff9c68b..8bf868242 100644 --- a/win-linux/extras/update-daemon/UpdateDaemon.pro +++ b/win-linux/extras/update-daemon/UpdateDaemon.pro @@ -4,7 +4,7 @@ include(common.pri) DEFINES += COPYRIGHT_YEAR=$${CURRENT_YEAR} DEFINES += APP_ICON_PATH=\"./icons/desktopeditors.ico\" -DEFINES += APP_LANG_PATH=\"./langs/langs.isl\" +DEFINES += APP_LANG_PATH=\"./langs/langs.bin\" ENV_URL_APPCAST_MAIN = $$(DESKTOP_URL_UPDATES_MAIN_CHANNEL) !isEmpty(ENV_URL_APPCAST_MAIN) { diff --git a/win-linux/extras/update-daemon/res/gresource.xml b/win-linux/extras/update-daemon/res/gresource.xml index 2d420e866..fa84b6574 100644 --- a/win-linux/extras/update-daemon/res/gresource.xml +++ b/win-linux/extras/update-daemon/res/gresource.xml @@ -1,7 +1,7 @@ - langs/langs.iss + langs/langs.bin diff --git a/win-linux/extras/update-daemon/res/langs/langs.bin b/win-linux/extras/update-daemon/res/langs/langs.bin new file mode 100644 index 0000000000000000000000000000000000000000..7a741e5c409aedc5abfcdfc96a0348045e961c9a GIT binary patch literal 49331 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 +# include # include -# include -# define istalnum(c) std::iswalnum(c) -# define istalpha(c) std::iswalpha(c) # define tistringstream std::wistringstream #else # include "platform_linux/utils.h" # include "res/gresource.c" -# include -# define istalnum(c) std::isalnum(c) -# define istalpha(c) std::isalpha(c) +# include # define tistringstream std::istringstream + typedef uint16_t WORD; #endif -bool isSeparator(tchar c) -{ - return c == _T(' ') || c == _T('\t') || c == _T('\r') || c == _T('\n'); -} - -bool isValidStringIdCharacter(tchar c) -{ - return istalnum(c) || istalpha(c) || c == _T('_'); -} - -bool isValidLocaleCharacter(tchar c) -{ - return istalpha(c) || c == _T('_'); -} - tstring getPrimaryLang(const tstring &lang, bool withScript = false) { if (lang.empty()) { @@ -54,11 +36,59 @@ tstring getPrimaryLang(const tstring &lang, bool withScript = false) } #ifdef _WIN32 +static IStream* LoadResourceToStream(int resourceId) +{ + IStream *pStream = nullptr; + HMODULE hInst = GetModuleHandle(nullptr); + if (HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resourceId), RT_RCDATA)) { + DWORD dataSize = SizeofResource(hInst, hRes); + if (dataSize > 0) { + if (HGLOBAL hResData = LoadResource(hInst, hRes)) { + if (LPVOID pData = LockResource(hResData)) { + if (HGLOBAL hGlobal = GlobalAlloc(GHND, dataSize)) { + if (LPVOID pBuffer = GlobalLock(hGlobal)) { + memcpy(pBuffer, pData, dataSize); + GlobalUnlock(hGlobal); + HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); + if (FAILED(hr)) { + GlobalFree(hGlobal); + pStream = nullptr; + } + } else { + GlobalFree(hGlobal); + } + } + } + FreeResource(hResData); + } + } + } + return pStream; +} + wstring StrToWStr(const string &str) { std::wstring_convert> converter; return converter.from_bytes(str); } +#else +static GInputStream* LoadResourceToStream(const char *resourcePath) +{ + GInputStream *pStream = nullptr; + if (GResource *res = gresource_get_resource()) { + g_resources_register(res); + if (GBytes *bytes = g_resource_lookup_data(res, resourcePath, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL)) { + gsize dataSize = 0; + const char *pData = (const char*)g_bytes_get_data(bytes, &dataSize); + if (dataSize > 0) { + pStream = g_memory_input_stream_new_from_data(pData, dataSize, NULL); + } + g_bytes_unref(bytes); + } + g_resource_unref(res); + } + return pStream; +} #endif Translator::Translator() : @@ -83,49 +113,145 @@ void Translator::init(const tstring &lang, const char *resourcePath) langName = lang; NS_Logger::WriteLog(_T("Current locale: ") + langName); + is_translations_valid = false; + const char ISL_MAGIC[] = "ISL"; #ifdef _WIN32 - HMODULE hInst = GetModuleHandle(NULL); - if (HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resourceId), RT_RCDATA)) { - if (HGLOBAL hResData = LoadResource(hInst, hRes)) { - if (LPVOID pData = LockResource(hResData)) { - DWORD dataSize = SizeofResource(hInst, hRes); - if (dataSize > 0) { - string text((const char*)pData, dataSize); - translations = StrToWStr(text); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); - FreeResource(hResData); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); + if (IStream *pStream = LoadResourceToStream(resourceId)) { + ULONG bytesRead = 0; + HRESULT hr = S_OK; + char magic[sizeof(ISL_MAGIC)] = { 0 }; + hr = pStream->Read(magic, sizeof(magic), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(magic) || strncmp(magic, ISL_MAGIC, sizeof(magic) - 1) != 0) { + pStream->Release(); + return; + } + WORD stringsMapSize = 0; + hr = pStream->Read(&stringsMapSize, sizeof(stringsMapSize), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(stringsMapSize)) { + pStream->Release(); + return; + } + for (WORD i = 0; i < stringsMapSize; i++) { + uint8_t stringIdLen = 0; + hr = pStream->Read(&stringIdLen, sizeof(stringIdLen), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(stringIdLen)) { + pStream->Release(); + return; + } + std::string stringId(stringIdLen, '\0'); + hr = pStream->Read(&stringId[0], stringIdLen, &bytesRead); + if (FAILED(hr) || bytesRead != stringIdLen) { + pStream->Release(); + return; + } + WORD localeMapSize = 0; + hr = pStream->Read(&localeMapSize, sizeof(localeMapSize), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(localeMapSize)) { + pStream->Release(); + return; + } + LocaleMap localeMap; + for (WORD j = 0; j < localeMapSize; j++) { + uint8_t localeLen = 0; + hr = pStream->Read(&localeLen, sizeof(localeLen), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(localeLen)) { + pStream->Release(); + return; + } + std::string localeName(localeLen, '\0'); + hr = pStream->Read(&localeName[0], localeLen, &bytesRead); + if (FAILED(hr) || bytesRead != localeLen) { + pStream->Release(); + return; + } + WORD translationLen = 0; + hr = pStream->Read(&translationLen, sizeof(translationLen), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(translationLen)) { + pStream->Release(); + return; + } + std::string translationString(translationLen, '\0'); + hr = pStream->Read(&translationString[0], translationLen, &bytesRead); + if (FAILED(hr) || bytesRead != translationLen) { + pStream->Release(); + return; + } + localeMap[StrToWStr(localeName)] = StrToWStr(translationString); + } + translMap[StrToWStr(stringId)] = localeMap; + } + pStream->Release(); + is_translations_valid = true; + } #else - if (GResource *res = gresource_get_resource()) { - g_resources_register(res); - if (GBytes *bytes = g_resource_lookup_data(res, resourcePath, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL)) { - gsize dataSize = 0; - const char *pData = (const char*)g_bytes_get_data(bytes, &dataSize); - if (dataSize > 0) { - string text(pData, dataSize); - translations = text; - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); - g_bytes_unref(bytes); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); - g_resource_unref(res); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); + if (GInputStream *pStream = LoadResourceToStream(resourcePath)) { + gsize bytesRead = 0; + gboolean hr = true; + char magic[sizeof(ISL_MAGIC)] = { 0 }; + hr = g_input_stream_read_all(pStream, magic, sizeof(magic), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(magic) || strncmp(magic, ISL_MAGIC, sizeof(magic) - 1) != 0) { + g_object_unref(pStream); + return; + } + WORD stringsMapSize = 0; + hr = g_input_stream_read_all(pStream, &stringsMapSize, sizeof(stringsMapSize), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(stringsMapSize)) { + g_object_unref(pStream); + return; + } + for (WORD i = 0; i < stringsMapSize; i++) { + uint8_t stringIdLen = 0; + hr = g_input_stream_read_all(pStream, &stringIdLen, sizeof(stringIdLen), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(stringIdLen)) { + g_object_unref(pStream); + return; + } + std::string stringId(stringIdLen, '\0'); + hr = g_input_stream_read_all(pStream, &stringId[0], stringIdLen, &bytesRead, NULL, NULL); + if (!hr || bytesRead != stringIdLen) { + g_object_unref(pStream); + return; + } + WORD localeMapSize = 0; + hr = g_input_stream_read_all(pStream, &localeMapSize, sizeof(localeMapSize), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(localeMapSize)) { + g_object_unref(pStream); + return; + } + LocaleMap localeMap; + for (WORD j = 0; j < localeMapSize; j++) { + uint8_t localeLen = 0; + hr = g_input_stream_read_all(pStream, &localeLen, sizeof(localeLen), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(localeLen)) { + g_object_unref(pStream); + return; + } + std::string localeName(localeLen, '\0'); + hr = g_input_stream_read_all(pStream, &localeName[0], localeLen, &bytesRead, NULL, NULL); + if (!hr || bytesRead != localeLen) { + g_object_unref(pStream); + return; + } + WORD translationLen = 0; + hr = g_input_stream_read_all(pStream, &translationLen, sizeof(translationLen), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(translationLen)) { + g_object_unref(pStream); + return; + } + std::string translationString(translationLen, '\0'); + hr = g_input_stream_read_all(pStream, &translationString[0], translationLen, &bytesRead, NULL, NULL); + if (!hr || bytesRead != translationLen) { + g_object_unref(pStream); + return; + } + localeMap[localeName] = translationString; + } + translMap[stringId] = localeMap; + } + g_object_unref(pStream); + is_translations_valid = true; + } #endif - - if (!translations.empty()) { - parseTranslations(); - if (!is_translations_valid) - NS_Logger::WriteLog(_T("Cannot parse translations, error in string: ") + error_substr + _T(" <---")); - } else - NS_Logger::WriteLog(_T("Error: translations is empty.")); } Translator::~Translator() @@ -161,145 +287,3 @@ void Translator::setLanguage(const tstring &lang) langName = lang; NS_Logger::WriteLog(_T("Current locale: ") + langName); } - -void Translator::parseTranslations() -{ - int token = TOKEN_BEGIN_DOCUMENT; - tstring stringId, currentLocale; - size_t pos = 0, len = translations.length(); - while (pos < len) { - size_t incr = 1; - tchar ch = translations.at(pos); - - switch (token) { - case TOKEN_BEGIN_DOCUMENT: - case TOKEN_END_VALUE: - if (!isSeparator(ch)) { - if (ch == _T(';')) { - // string is comment - size_t end = translations.find_first_of(_T('\n'), pos); - incr = (end == tstring::npos) ? len - pos : end - pos + 1; - } else { - size_t end; - for (end = pos; end < len; end++) { - tchar c = translations.at(end); - if (!isValidLocaleCharacter(c)) - break; - } - size_t locale_len = end - pos; - if (locale_len < 12 && locale_len != 0 && locale_len != 1 && locale_len != 4 && locale_len != 9) { - token = TOKEN_BEGIN_LOCALE; - continue; - } else { - // TOKEN_ERROR - error_substr = translations.substr(0, pos + 1); - return; - } - } - } - break; - - case TOKEN_BEGIN_STRING_ID: - if (!isSeparator(ch)) { - size_t end; - tchar c; - for (end = pos; end < len; end++) { - c = translations.at(end); - if (!isValidStringIdCharacter(c)) - break; - } - c = translations.at(end); - if (end < len && !isSeparator(c) && c != _T('=')) { - // TOKEN_ERROR - error_substr = translations.substr(0, end + 1); - return; - } - stringId = translations.substr(pos, end - pos); - if (!stringId.empty() && translMap.find(stringId) == translMap.end()) - translMap[stringId] = LocaleMap(); - - token = TOKEN_END_STRING_ID; - incr = end - pos; - } - break; - - case TOKEN_END_STRING_ID: - if (!isSeparator(ch)) { - if (ch == _T('=')) { - token = TOKEN_BEGIN_VALUE; - } else { - // TOKEN_ERROR - error_substr = translations.substr(0, pos + 1); - return; - } - } - break; - - case TOKEN_BEGIN_LOCALE: { - size_t end; - for (end = pos; end < len; end++) { - tchar c = translations.at(end); - if (!isValidLocaleCharacter(c)) - break; - } - size_t locale_len = end - pos; - currentLocale = translations.substr(pos, locale_len); - if (pos + locale_len == len) { - error_substr = translations.substr(0, pos + locale_len); - return; - } - token = TOKEN_END_LOCALE; - incr = locale_len; - break; - } - - case TOKEN_END_LOCALE: - if (!isSeparator(ch)) { - if (ch == _T('.')) { - token = TOKEN_BEGIN_STRING_ID; - } else { - // TOKEN_ERROR - error_substr = translations.substr(0, pos + 1); - return; - } - } - break; - - case TOKEN_BEGIN_VALUE: { - size_t end = translations.find_first_of(_T('\n'), pos); - tstring val; - if (end == tstring::npos) { - val = translations.substr(pos); - incr = len - pos; - } else { - val = translations.substr(pos, end - pos); - incr = end - pos; - } - - if (!val.empty() && val.back() == _T('\r')) - val.pop_back(); - - size_t p = val.find(_T("\\n")); - while (p != std::string::npos) { - val.replace(p, 2, _T("\\")); - val[p] = _T('\n'); - p = val.find(_T("\\n"), p + 1); - } - if (!currentLocale.empty() && translMap.find(stringId) != translMap.end()) - translMap[stringId][currentLocale] = val; - - token = TOKEN_END_VALUE; - break; - } - - default: - break; - } - pos += incr; - if (pos == len) - token = TOKEN_END_DOCUMENT; - } - - if (token == TOKEN_END_DOCUMENT) - is_translations_valid = true; -} diff --git a/win-linux/extras/update-daemon/src/classes/translator.h b/win-linux/extras/update-daemon/src/classes/translator.h index 2492beb17..0d8a99564 100644 --- a/win-linux/extras/update-daemon/src/classes/translator.h +++ b/win-linux/extras/update-daemon/src/classes/translator.h @@ -40,11 +40,7 @@ private: Translator(); ~Translator(); - void parseTranslations(); - TranslationsMap translMap; - tstring translations, - error_substr; tstring langName; bool is_translations_valid; diff --git a/win-linux/extras/update-daemon/src/platform_linux/main.cpp b/win-linux/extras/update-daemon/src/platform_linux/main.cpp index e730ed608..70945483d 100644 --- a/win-linux/extras/update-daemon/src/platform_linux/main.cpp +++ b/win-linux/extras/update-daemon/src/platform_linux/main.cpp @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) if (NS_Utils::cmdArgContains("--log")) NS_Logger::AllowWriteLog(); std::locale::global(std::locale("")); - Translator::instance().init(NS_Utils::GetAppLanguage(), "/langs/langs.isl"); + Translator::instance().init(NS_Utils::GetAppLanguage(), "/langs/langs.bin"); CSocket socket(0, INSTANCE_SVC_PORT); if (!socket.isPrimaryInstance()) return 0; From 16cbeb420bc35b5b9775a00bc3558350ea772071 Mon Sep 17 00:00:00 2001 From: SimplestStudio Date: Wed, 18 Jun 2025 23:08:39 +0300 Subject: [PATCH 7/7] [win-linux] updatesvc: normalize locale name format --- win-linux/extras/update-daemon/src/classes/translator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/win-linux/extras/update-daemon/src/classes/translator.cpp b/win-linux/extras/update-daemon/src/classes/translator.cpp index 2b209fc7a..3f0c4ef3b 100644 --- a/win-linux/extras/update-daemon/src/classes/translator.cpp +++ b/win-linux/extras/update-daemon/src/classes/translator.cpp @@ -1,5 +1,6 @@ #include "translator.h" #include +#include #ifdef _WIN32 # include "platform_win/resource.h" # include "platform_win/utils.h" @@ -111,6 +112,7 @@ void Translator::init(const tstring &lang, const char *resourcePath) #endif { langName = lang; + std::replace(langName.begin(), langName.end(), '-', '_'); NS_Logger::WriteLog(_T("Current locale: ") + langName); is_translations_valid = false; @@ -285,5 +287,6 @@ tstring Translator::tr(const tchar *str) const void Translator::setLanguage(const tstring &lang) { langName = lang; + std::replace(langName.begin(), langName.end(), '-', '_'); NS_Logger::WriteLog(_T("Current locale: ") + langName); }