6#include "GuiTextTranslator.h"
12#define HASHWORDBITS 32
14GuiTextTranslator::~GuiTextTranslator() {
26#if defined(__GNUC__) && (__GNUC__ >= 8)
27#define GUI_OPAQUE_CALL __attribute__((noipa, cold))
29#define GUI_OPAQUE_CALL __attribute__((noinline, noclone, cold))
32[[noreturn]] GUI_OPAQUE_CALL
33void GuiTextTranslator::fatalCorruption(
const GuiTextTranslator*,
const void*,
const void*, uint32_t) {
37void GuiTextTranslator::checkIntegrity(
const MSG* node,
const MSG* prev)
const {
39 fatalCorruption(
this, node, prev, magic);
42 if (
reinterpret_cast<uintptr_t
>(node) & (
alignof(MSG) - 1))
43 fatalCorruption(
this, node, prev, magic);
47uint32_t GuiTextTranslator::hashString(
const char *str_param) {
49 const char *str = str_param;
51 while (*str !=
'\0') {
53 hval += (uint8_t) * str++;
54 g = hval & ((uint32_t) 0xf << (HASHWORDBITS - 4));
56 hval ^= g >> (HASHWORDBITS - 8);
64char* GuiTextTranslator::expandEscape(
const char *str) {
68 retval = (
char*) malloc(strlen(str) + 1);
69 if (retval ==
nullptr)
74 while (cp[0] !=
'\0' && cp[0] !=
'\\')
127 int ch = *cp++ -
'0';
128 if (*cp >=
'0' && *cp <=
'7') {
132 if (*cp >=
'0' && *cp <=
'7') {
145 while (cp[0] !=
'\0' && cp[0] !=
'\\')
148 }
while (cp[0] !=
'\0');
150 terminate: *rp =
'\0';
154GuiTextTranslator::MSG* GuiTextTranslator::findMessage(uint32_t
id)
const {
155 checkIntegrity(head,
nullptr);
157 const MSG *prev =
nullptr;
158 for (MSG *msg = head; msg; prev = msg, msg = msg->next) {
159 checkIntegrity(msg, prev);
166GuiTextTranslator::MSG* GuiTextTranslator::setMessage(
const char *msgid,
const char *msgstr) {
167 uint32_t
id = hashString(msgid);
168 MSG *msg = findMessage(
id);
173 msg->msgstr =
nullptr;
183 msg->msgstr = expandEscape(msgstr);
190 checkIntegrity(head,
nullptr);
193 checkIntegrity(head,
nullptr);
194 MSG *nextMsg = head->next;
202const char* GuiTextTranslator::memFGets(
char *dst,
int maxlen,
const char *src,
const char *eof)
const {
203 if (!src || !dst || maxlen <= 0 || src >= eof)
207 while (src + len < eof && src[len] !=
'\n' && len < maxlen - 1) {
214 if (src + len < eof && src[len] ==
'\n') {
215 return src + len + 1;
224 if (!buffer || size == 0)
228 char *lastID =
nullptr;
229 const char *ptr =
reinterpret_cast<const char*
>(buffer);
230 const char *eof = ptr + size;
232 while (ptr && ptr < eof) {
233 const char *nextPtr = memFGets(line,
sizeof(line), ptr, eof);
235 if (!nextPtr && ptr == nextPtr)
241 if (line[0] ==
'#' || line[0] ==
'\0')
244 if (strncmp(line,
"msgid \"", 7) == 0) {
250 char *msgid = &line[7];
251 char *end = strrchr(msgid,
'"');
252 if (end && (end - msgid) >= 0) {
254 lastID = strdup(msgid);
256 }
else if (strncmp(line,
"msgstr \"", 8) == 0) {
260 char *msgstr = &line[8];
261 char *end = strrchr(msgstr,
'"');
262 if (end && (end - msgstr) >= 0) {
264 setMessage(lastID, msgstr);
283 MSG *msg = findMessage(hashString(msgid));
285 if (msg && msg->msgstr && msg->msgstr[0] !=
'\0') {
Hashed msgid -> UTF-8 string table loaded from a single binary .lang blob (see loadLanguage()).
const char * getText(const char *msgid) const
Retrieves the translated UTF-8 string for a given ASCII message ID.
bool loadLanguage(const uint8_t *buffer, size_t size)
Loads and parses a translation file from a memory buffer.
void clear()
Clears the current translation dictionary and frees memory.