2 changed files with 225 additions and 225 deletions
@ -1,217 +1,217 @@
|
||||
#include "options.h" |
||||
#include "utils/paths.h" |
||||
#include "utils/utf8.h" |
||||
#include <map> |
||||
|
||||
using namespace devilution; |
||||
#define MO_MAGIC 0x950412de |
||||
|
||||
namespace { |
||||
|
||||
struct cstring_cmp { |
||||
bool operator()(const char *s1, const char *s2) const |
||||
{ |
||||
return strcmp(s1, s2) < 0; |
||||
} |
||||
}; |
||||
|
||||
std::map<const char *, const char *, cstring_cmp> map; |
||||
std::map<const char *, const char *, cstring_cmp> meta; |
||||
|
||||
struct mo_head { |
||||
uint32_t magic; |
||||
struct { |
||||
uint16_t major; |
||||
uint16_t minor; |
||||
} revision; |
||||
|
||||
uint32_t nb_mappings; |
||||
uint32_t src_offset; |
||||
uint32_t dst_offset; |
||||
}; |
||||
|
||||
struct mo_entry { |
||||
uint32_t length; |
||||
uint32_t offset; |
||||
}; |
||||
|
||||
char *strtrim_left(char *s) |
||||
{ |
||||
while (*s && isblank(*s)) { |
||||
s++; |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
char *strtrim_right(char *s) |
||||
{ |
||||
size_t length = strlen(s); |
||||
|
||||
while (length) { |
||||
length--; |
||||
if (isblank(s[length])) { |
||||
s[length] = '\0'; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
bool parse_metadata(char *data) |
||||
{ |
||||
char *key, *delim, *val; |
||||
char *ptr = data; |
||||
bool utf8 = false; |
||||
|
||||
while (ptr && (delim = strstr(ptr, ":"))) { |
||||
key = strtrim_left(ptr); |
||||
val = strtrim_left(delim + 1); |
||||
|
||||
// null-terminate key
|
||||
*delim = '\0'; |
||||
|
||||
// progress to next line (if any)
|
||||
if ((ptr = strstr(val, "\n"))) { |
||||
*ptr = '\0'; |
||||
ptr++; |
||||
} |
||||
|
||||
val = strtrim_right(val); |
||||
meta[key] = val; |
||||
|
||||
// Match "Content-Type: text/plain; charset=UTF-8"
|
||||
if (!strcmp("Content-Type", key) && (delim = strstr(val, "="))) { |
||||
utf8 = !strcasecmp(delim + 1, "utf-8"); |
||||
} |
||||
} |
||||
|
||||
return utf8; |
||||
} |
||||
|
||||
char *read_entry(FILE *fp, mo_entry *e) |
||||
{ |
||||
void *data; |
||||
|
||||
if (fseek(fp, e->offset, SEEK_SET)) { |
||||
return nullptr; |
||||
} |
||||
|
||||
if (!(data = calloc(e->length + 1, sizeof(char)))) { |
||||
return nullptr; |
||||
} |
||||
|
||||
if (fread(data, sizeof(char), e->length, fp) != e->length) { |
||||
free(data); |
||||
return nullptr; |
||||
} |
||||
|
||||
return static_cast<char *>(data); |
||||
} |
||||
} // namespace
|
||||
|
||||
const char *LanguageTranslate(const char *key) |
||||
{ |
||||
auto it = map.find(key); |
||||
if (it == map.end()) { |
||||
char *val; |
||||
if (!(val = (char *)calloc(strlen(key) + 1, sizeof(char)))) { |
||||
return key; |
||||
} |
||||
std::string latin1 = utf8_to_latin1(key); |
||||
strcpy(val, latin1.c_str()); |
||||
map[key] = val; |
||||
return val; |
||||
} |
||||
|
||||
return it->second; |
||||
} |
||||
|
||||
const char *LanguageMetadata(const char *key) |
||||
{ |
||||
auto it = meta.find(key); |
||||
if (it == meta.end()) { |
||||
return nullptr; |
||||
} |
||||
|
||||
return it->second; |
||||
} |
||||
|
||||
void LanguageInitialize() |
||||
{ |
||||
mo_entry *src, *dst; |
||||
mo_head head; |
||||
FILE *fp; |
||||
bool utf8; |
||||
|
||||
auto path = paths::LangPath() + "./" + sgOptions.Language.szCode + ".gmo"; |
||||
if (!(fp = fopen(path.c_str(), "rb"))) { |
||||
path = paths::LangPath() + "./" + sgOptions.Language.szCode + ".mo"; |
||||
if (!(fp = fopen(path.c_str(), "rb"))) { |
||||
perror(path.c_str()); |
||||
return; |
||||
} |
||||
} |
||||
// Read header and do sanity checks
|
||||
if (fread(&head, sizeof(mo_head), 1, fp) != 1) { |
||||
return; |
||||
} |
||||
|
||||
if (head.magic != MO_MAGIC) { |
||||
return; // not a MO file
|
||||
} |
||||
|
||||
if (head.revision.major > 1 || head.revision.minor > 1) { |
||||
return; // unsupported revision
|
||||
} |
||||
|
||||
// Read entries of source strings
|
||||
src = new mo_entry[head.nb_mappings]; |
||||
if (fseek(fp, head.src_offset, SEEK_SET)) { |
||||
delete[] src; |
||||
return; |
||||
} |
||||
if (fread(src, sizeof(mo_entry), head.nb_mappings, fp) != head.nb_mappings) { |
||||
delete[] src; |
||||
return; |
||||
} |
||||
|
||||
// Read entries of target strings
|
||||
dst = new mo_entry[head.nb_mappings]; |
||||
if (fseek(fp, head.dst_offset, SEEK_SET)) { |
||||
delete[] dst; |
||||
delete[] src; |
||||
return; |
||||
} |
||||
|
||||
if (fread(dst, sizeof(mo_entry), head.nb_mappings, fp) != head.nb_mappings) { |
||||
delete[] dst; |
||||
delete[] src; |
||||
return; |
||||
} |
||||
|
||||
// Read strings described by entries
|
||||
for (uint32_t i = 0; i < head.nb_mappings; i++) { |
||||
char *key, *val; |
||||
if ((key = read_entry(fp, src + i))) { |
||||
if ((val = read_entry(fp, dst + i))) { |
||||
if (!*key) { |
||||
utf8 = parse_metadata(val); |
||||
} else { |
||||
if (utf8) { |
||||
std::string latin1 = utf8_to_latin1(key); |
||||
strcpy(key, latin1.c_str()); |
||||
|
||||
latin1 = utf8_to_latin1(val); |
||||
strcpy(val, latin1.c_str()); |
||||
} |
||||
map[key] = val; |
||||
} |
||||
} else { |
||||
free(key); |
||||
} |
||||
} |
||||
} |
||||
delete[] dst; |
||||
delete[] src; |
||||
} |
||||
#include "options.h" |
||||
#include "utils/paths.h" |
||||
#include "utils/utf8.h" |
||||
#include <map> |
||||
|
||||
using namespace devilution; |
||||
#define MO_MAGIC 0x950412de |
||||
|
||||
namespace { |
||||
|
||||
struct cstring_cmp { |
||||
bool operator()(const char *s1, const char *s2) const |
||||
{ |
||||
return strcmp(s1, s2) < 0; |
||||
} |
||||
}; |
||||
|
||||
std::map<const char *, const char *, cstring_cmp> map; |
||||
std::map<const char *, const char *, cstring_cmp> meta; |
||||
|
||||
struct mo_head { |
||||
uint32_t magic; |
||||
struct { |
||||
uint16_t major; |
||||
uint16_t minor; |
||||
} revision; |
||||
|
||||
uint32_t nb_mappings; |
||||
uint32_t src_offset; |
||||
uint32_t dst_offset; |
||||
}; |
||||
|
||||
struct mo_entry { |
||||
uint32_t length; |
||||
uint32_t offset; |
||||
}; |
||||
|
||||
char *strtrim_left(char *s) |
||||
{ |
||||
while (*s && isblank(*s)) { |
||||
s++; |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
char *strtrim_right(char *s) |
||||
{ |
||||
size_t length = strlen(s); |
||||
|
||||
while (length) { |
||||
length--; |
||||
if (isblank(s[length])) { |
||||
s[length] = '\0'; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
bool parse_metadata(char *data) |
||||
{ |
||||
char *key, *delim, *val; |
||||
char *ptr = data; |
||||
bool utf8 = false; |
||||
|
||||
while (ptr && (delim = strstr(ptr, ":"))) { |
||||
key = strtrim_left(ptr); |
||||
val = strtrim_left(delim + 1); |
||||
|
||||
// null-terminate key
|
||||
*delim = '\0'; |
||||
|
||||
// progress to next line (if any)
|
||||
if ((ptr = strstr(val, "\n"))) { |
||||
*ptr = '\0'; |
||||
ptr++; |
||||
} |
||||
|
||||
val = strtrim_right(val); |
||||
meta[key] = val; |
||||
|
||||
// Match "Content-Type: text/plain; charset=UTF-8"
|
||||
if (!strcmp("Content-Type", key) && (delim = strstr(val, "="))) { |
||||
utf8 = !strcasecmp(delim + 1, "utf-8"); |
||||
} |
||||
} |
||||
|
||||
return utf8; |
||||
} |
||||
|
||||
char *read_entry(FILE *fp, mo_entry *e) |
||||
{ |
||||
void *data; |
||||
|
||||
if (fseek(fp, e->offset, SEEK_SET)) { |
||||
return nullptr; |
||||
} |
||||
|
||||
if (!(data = calloc(e->length + 1, sizeof(char)))) { |
||||
return nullptr; |
||||
} |
||||
|
||||
if (fread(data, sizeof(char), e->length, fp) != e->length) { |
||||
free(data); |
||||
return nullptr; |
||||
} |
||||
|
||||
return static_cast<char *>(data); |
||||
} |
||||
} // namespace
|
||||
|
||||
const char *LanguageTranslate(const char *key) |
||||
{ |
||||
auto it = map.find(key); |
||||
if (it == map.end()) { |
||||
char *val; |
||||
if (!(val = (char *)calloc(strlen(key) + 1, sizeof(char)))) { |
||||
return key; |
||||
} |
||||
std::string latin1 = utf8_to_latin1(key); |
||||
strcpy(val, latin1.c_str()); |
||||
map[key] = val; |
||||
return val; |
||||
} |
||||
|
||||
return it->second; |
||||
} |
||||
|
||||
const char *LanguageMetadata(const char *key) |
||||
{ |
||||
auto it = meta.find(key); |
||||
if (it == meta.end()) { |
||||
return nullptr; |
||||
} |
||||
|
||||
return it->second; |
||||
} |
||||
|
||||
void LanguageInitialize() |
||||
{ |
||||
mo_entry *src, *dst; |
||||
mo_head head; |
||||
FILE *fp; |
||||
bool utf8; |
||||
|
||||
auto path = paths::LangPath() + "./" + sgOptions.Language.szCode + ".gmo"; |
||||
if (!(fp = fopen(path.c_str(), "rb"))) { |
||||
path = paths::LangPath() + "./" + sgOptions.Language.szCode + ".mo"; |
||||
if (!(fp = fopen(path.c_str(), "rb"))) { |
||||
perror(path.c_str()); |
||||
return; |
||||
} |
||||
} |
||||
// Read header and do sanity checks
|
||||
if (fread(&head, sizeof(mo_head), 1, fp) != 1) { |
||||
return; |
||||
} |
||||
|
||||
if (head.magic != MO_MAGIC) { |
||||
return; // not a MO file
|
||||
} |
||||
|
||||
if (head.revision.major > 1 || head.revision.minor > 1) { |
||||
return; // unsupported revision
|
||||
} |
||||
|
||||
// Read entries of source strings
|
||||
src = new mo_entry[head.nb_mappings]; |
||||
if (fseek(fp, head.src_offset, SEEK_SET)) { |
||||
delete[] src; |
||||
return; |
||||
} |
||||
if (fread(src, sizeof(mo_entry), head.nb_mappings, fp) != head.nb_mappings) { |
||||
delete[] src; |
||||
return; |
||||
} |
||||
|
||||
// Read entries of target strings
|
||||
dst = new mo_entry[head.nb_mappings]; |
||||
if (fseek(fp, head.dst_offset, SEEK_SET)) { |
||||
delete[] dst; |
||||
delete[] src; |
||||
return; |
||||
} |
||||
|
||||
if (fread(dst, sizeof(mo_entry), head.nb_mappings, fp) != head.nb_mappings) { |
||||
delete[] dst; |
||||
delete[] src; |
||||
return; |
||||
} |
||||
|
||||
// Read strings described by entries
|
||||
for (uint32_t i = 0; i < head.nb_mappings; i++) { |
||||
char *key, *val; |
||||
if ((key = read_entry(fp, src + i))) { |
||||
if ((val = read_entry(fp, dst + i))) { |
||||
if (!*key) { |
||||
utf8 = parse_metadata(val); |
||||
} else { |
||||
if (utf8) { |
||||
std::string latin1 = utf8_to_latin1(key); |
||||
strcpy(key, latin1.c_str()); |
||||
|
||||
latin1 = utf8_to_latin1(val); |
||||
strcpy(val, latin1.c_str()); |
||||
} |
||||
map[key] = val; |
||||
} |
||||
} else { |
||||
free(key); |
||||
} |
||||
} |
||||
} |
||||
delete[] dst; |
||||
delete[] src; |
||||
} |
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#pragma once |
||||
|
||||
#define _(x) LanguageTranslate(x) |
||||
#define N_(x) (x) |
||||
|
||||
void LanguageInitialize(); |
||||
const char* LanguageTranslate(const char* key); |
||||
const char* LanguageMetadata(const char *key); |
||||
#pragma once |
||||
|
||||
#define _(x) LanguageTranslate(x) |
||||
#define N_(x) (x) |
||||
|
||||
void LanguageInitialize(); |
||||
const char* LanguageTranslate(const char* key); |
||||
const char* LanguageMetadata(const char *key); |
||||
|
||||
Loading…
Reference in new issue