vbagx/source/utils/gettext.cpp

288 lines
5.4 KiB
C++

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <gctypes.h>
#include <unistd.h>
#include "gettext.h"
#include "filelist.h"
#include "vbagx.h"
typedef struct _MSG
{
u32 id;
char* msgstr;
struct _MSG *next;
} MSG;
static MSG *baseMSG = 0;
#define HASHWORDBITS 32
/* Defines the so called `hashpjw' function by P.J. Weinberger
[see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
1986, 1987 Bell Telephone Laboratories, Inc.] */
static inline u32 hash_string(const char *str_param)
{
u32 hval, g;
const char *str = str_param;
/* Compute the hash value for the given string. */
hval = 0;
while (*str != '\0')
{
hval <<= 4;
hval += (u8) * str++;
g = hval & ((u32) 0xf << (HASHWORDBITS - 4));
if (g != 0)
{
hval ^= g >> (HASHWORDBITS - 8);
hval ^= g;
}
}
return hval;
}
/* Expand some escape sequences found in the argument string. */
static char *
expand_escape(const char *str)
{
char *retval, *rp;
const char *cp = str;
retval = (char *) malloc(strlen(str) + 1);
if (retval == NULL)
return NULL;
rp = retval;
while (cp[0] != '\0' && cp[0] != '\\')
*rp++ = *cp++;
if (cp[0] == '\0')
goto terminate;
do
{
/* Here cp[0] == '\\'. */
switch (*++cp)
{
case '\"': /* " */
*rp++ = '\"';
++cp;
break;
case 'a': /* alert */
*rp++ = '\a';
++cp;
break;
case 'b': /* backspace */
*rp++ = '\b';
++cp;
break;
case 'f': /* form feed */
*rp++ = '\f';
++cp;
break;
case 'n': /* new line */
*rp++ = '\n';
++cp;
break;
case 'r': /* carriage return */
*rp++ = '\r';
++cp;
break;
case 't': /* horizontal tab */
*rp++ = '\t';
++cp;
break;
case 'v': /* vertical tab */
*rp++ = '\v';
++cp;
break;
case '\\':
*rp = '\\';
++cp;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
{
int ch = *cp++ - '0';
if (*cp >= '0' && *cp <= '7')
{
ch *= 8;
ch += *cp++ - '0';
if (*cp >= '0' && *cp <= '7')
{
ch *= 8;
ch += *cp++ - '0';
}
}
*rp = ch;
}
break;
default:
*rp = '\\';
break;
}
while (cp[0] != '\0' && cp[0] != '\\')
*rp++ = *cp++;
} while (cp[0] != '\0');
/* Terminate string. */
terminate: *rp = '\0';
return retval;
}
static MSG *findMSG(u32 id)
{
MSG *msg;
for (msg = baseMSG; msg; msg = msg->next)
{
if (msg->id == id)
return msg;
}
return NULL;
}
static MSG *setMSG(const char *msgid, const char *msgstr)
{
u32 id = hash_string(msgid);
MSG *msg = findMSG(id);
if (!msg)
{
msg = (MSG *) malloc(sizeof(MSG));
msg->id = id;
msg->msgstr = NULL;
msg->next = baseMSG;
baseMSG = msg;
}
if (msg)
{
if (msgstr)
{
if (msg->msgstr)
free(msg->msgstr);
//msg->msgstr = strdup(msgstr);
msg->msgstr = expand_escape(msgstr);
}
return msg;
}
return NULL;
}
static void gettextCleanUp(void)
{
while (baseMSG)
{
MSG *nextMsg = baseMSG->next;
free(baseMSG->msgstr);
free(baseMSG);
baseMSG = nextMsg;
}
}
static char * memfgets(char * dst, int maxlen, char * src)
{
if(!src || !dst || maxlen <= 0)
return NULL;
char * newline = strchr(src, '\n');
if(newline == NULL)
return NULL;
memcpy(dst, src, (newline-src));
dst[(newline-src)] = 0;
return ++newline;
}
bool LoadLanguage()
{
char line[200];
char *lastID = NULL;
char *file, *eof;
switch(GCSettings.language)
{
case LANG_JAPANESE: file = (char *)jp_lang; eof = file + jp_lang_size; break;
case LANG_ENGLISH: file = (char *)en_lang; eof = file + en_lang_size; break;
case LANG_GERMAN: file = (char *)de_lang; eof = file + de_lang_size; break;
case LANG_FRENCH: file = (char *)fr_lang; eof = file + fr_lang_size; break;
case LANG_SPANISH: file = (char *)es_lang; eof = file + es_lang_size; break;
case LANG_ITALIAN: file = (char *)it_lang; eof = file + it_lang_size; break;
case LANG_DUTCH: file = (char *)nl_lang; eof = file + nl_lang_size; break;
case LANG_SIMP_CHINESE:
case LANG_TRAD_CHINESE: file = (char *)zh_lang; eof = file + zh_lang_size; break;
case LANG_KOREAN: file = (char *)ko_lang; eof = file + ko_lang_size; break;
case LANG_PORTUGUESE: file = (char *)pt_lang; eof = file + pt_lang_size; break;
default: return false;
}
gettextCleanUp();
while (file && file < eof)
{
file = memfgets(line, sizeof(line), file);
if(!file)
break;
// lines starting with # are comments
if (line[0] == '#')
continue;
if (strncmp(line, "msgid \"", 7) == 0)
{
char *msgid, *end;
if (lastID)
{
free(lastID);
lastID = NULL;
}
msgid = &line[7];
end = strrchr(msgid, '"');
if (end && end - msgid > 1)
{
*end = 0;
lastID = strdup(msgid);
}
}
else if (strncmp(line, "msgstr \"", 8) == 0)
{
char *msgstr, *end;
if (lastID == NULL)
continue;
msgstr = &line[8];
end = strrchr(msgstr, '"');
if (end && end - msgstr > 1)
{
*end = 0;
setMSG(lastID, msgstr);
}
free(lastID);
lastID = NULL;
}
}
return true;
}
const char *gettext(const char *msgid)
{
MSG *msg = findMSG(hash_string(msgid));
if (msg && msg->msgstr)
{
return msg->msgstr;
}
return msgid;
}