mirror of
https://github.com/voiceip/tinyphone
synced 2026-08-07 02:26:03 -04:00
392 lines
10 KiB
C++
392 lines
10 KiB
C++
#include "StdAfx.h"
|
|
#include "langpack.h"
|
|
#include "utf.h"
|
|
#include "Strsafe.h"
|
|
|
|
LangPackStruct langPack;
|
|
|
|
static void TrimString(char *str)
|
|
{
|
|
size_t start, len = strlen(str);
|
|
while(str[0] && (unsigned char)str[len-1] <= ' ') str[--len] = 0;
|
|
for (start = 0; str[start] && (unsigned char)str[start] <= ' '; start++);
|
|
memmove(str, str + start, len - start + 1);
|
|
}
|
|
|
|
static void TrimStringSimple(char *str)
|
|
{
|
|
size_t len = strlen(str);
|
|
if (str[len-1] == '\n') str[--len] = '\0';
|
|
if (str[len-1] == '\r') str[len-1] = '\0';
|
|
}
|
|
|
|
static int IsEmpty(char *str)
|
|
{
|
|
int i = 0;
|
|
|
|
while (str[i])
|
|
{
|
|
if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n')
|
|
return 0;
|
|
i++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void ConvertBackslashes(char *str, UINT fileCp)
|
|
{
|
|
char *pstr;
|
|
for (pstr = str; *pstr; pstr = CharNextExA(fileCp, pstr, 0))
|
|
{
|
|
if( *pstr == '\\' )
|
|
{
|
|
switch( pstr[1] )
|
|
{
|
|
case 'n': *pstr = '\n'; break;
|
|
case 't': *pstr = '\t'; break;
|
|
case 'r': *pstr = '\r'; break;
|
|
default: *pstr = pstr[1]; break;
|
|
}
|
|
memmove(pstr+1, pstr+2, strlen(pstr+2) + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
#pragma optimize( "gt", on )
|
|
#endif
|
|
|
|
// MurmurHash2
|
|
unsigned int __fastcall hash(const void * key, unsigned int len)
|
|
{
|
|
// 'm' and 'r' are mixing constants generated offline.
|
|
// They're not really 'magic', they just happen to work well.
|
|
const unsigned int m = 0x5bd1e995;
|
|
const int r = 24;
|
|
|
|
// Initialize the hash to a 'random' value
|
|
unsigned int h = len;
|
|
|
|
// Mix 4 bytes at a time into the hash
|
|
const unsigned char * data = (const unsigned char *)key;
|
|
|
|
while(len >= 4)
|
|
{
|
|
unsigned int k = *(unsigned int *)data;
|
|
|
|
k *= m;
|
|
k ^= k >> r;
|
|
k *= m;
|
|
|
|
h *= m;
|
|
h ^= k;
|
|
|
|
data += 4;
|
|
len -= 4;
|
|
}
|
|
|
|
// Handle the last few bytes of the input array
|
|
switch(len)
|
|
{
|
|
case 3: h ^= data[2] << 16;
|
|
case 2: h ^= data[1] << 8;
|
|
case 1: h ^= data[0];
|
|
h *= m;
|
|
};
|
|
|
|
// Do a few final mixes of the hash to ensure the last few
|
|
// bytes are well-incorporated.
|
|
h ^= h >> 13;
|
|
h *= m;
|
|
h ^= h >> 15;
|
|
|
|
return h;
|
|
}
|
|
|
|
unsigned int __fastcall hashstrW(const char * key)
|
|
{
|
|
if (key == NULL) return 0;
|
|
const unsigned int len = (unsigned int)wcslen((const wchar_t*)key);
|
|
char* buf = (char*)alloca(len + 1);
|
|
for (unsigned i = 0; i <= len ; ++i)
|
|
buf[i] = key[i << 1];
|
|
return hash(buf, len);
|
|
}
|
|
|
|
__inline unsigned int hashstr(const char * key)
|
|
{
|
|
if (key == NULL) return 0;
|
|
const unsigned int len = (unsigned int)strlen((const char*)key);
|
|
return hash(key, len);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
#pragma optimize( "", on )
|
|
#endif
|
|
|
|
static int SortLangPackHashesProc(struct LangPackEntry *arg1,struct LangPackEntry *arg2)
|
|
{
|
|
if(arg1->englishHash<arg2->englishHash) return -1;
|
|
if(arg1->englishHash>arg2->englishHash) return 1;
|
|
/* both source strings of the same hash (may not be the same string thou) put
|
|
the one that was written first to be found first */
|
|
if(arg1->linePos<arg2->linePos) return -1;
|
|
if(arg1->linePos>arg2->linePos) return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int SortLangPackHashesProc2(struct LangPackEntry *arg1,struct LangPackEntry *arg2)
|
|
{
|
|
if(arg1->englishHash<arg2->englishHash) return -1;
|
|
if(arg1->englishHash>arg2->englishHash) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int LoadLangPack(const TCHAR *szLangPack)
|
|
{
|
|
FILE *fp;
|
|
char line[4096] = "";
|
|
char *pszColon;
|
|
char *pszLine;
|
|
int entriesAlloced;
|
|
int startOfLine=0;
|
|
unsigned int linePos=1;
|
|
LCID langID;
|
|
UINT fileCp = CP_ACP;
|
|
|
|
lstrcpy(langPack.filename,szLangPack);
|
|
fp = _tfopen(szLangPack,_T("rt"));
|
|
if(fp==NULL) return 1;
|
|
fgets(line,sizeof(line),fp);
|
|
size_t lineLen = strlen(line);
|
|
if (lineLen >= 3 && line[0]=='\xef' && line[1]=='\xbb' && line[2]=='\xbf')
|
|
{
|
|
fileCp = CP_UTF8;
|
|
memmove(line, line + 3, lineLen - 2);
|
|
}
|
|
TrimString(line);
|
|
if(lstrcmpA(line,"Language Pack")) {fclose(fp); return 2;}
|
|
//headers
|
|
while(!feof(fp)) {
|
|
startOfLine=ftell(fp);
|
|
if(fgets(line,sizeof(line),fp)==NULL) break;
|
|
TrimString(line);
|
|
if(IsEmpty(line) || line[0]==';' || line[0]==0) continue;
|
|
if(line[0]=='[') break;
|
|
pszColon=strchr(line,':');
|
|
if(pszColon==NULL) {fclose(fp); return 3;}
|
|
*pszColon=0;
|
|
if(!lstrcmpA(line,"Language")) {_snprintf(langPack.language,sizeof(langPack.language),"%s",pszColon+1); TrimString(langPack.language);}
|
|
else if(!lstrcmpA(line,"Last-Modified-Using")) {_snprintf(langPack.lastModifiedUsing,sizeof(langPack.lastModifiedUsing),"%s",pszColon+1); TrimString(langPack.lastModifiedUsing);}
|
|
else if(!lstrcmpA(line,"Authors")) {_snprintf(langPack.authors,sizeof(langPack.authors),"%s",pszColon+1); TrimString(langPack.authors);}
|
|
else if(!lstrcmpA(line,"Author-email")) {_snprintf(langPack.authorEmail,sizeof(langPack.authorEmail),"%s",pszColon+1); TrimString(langPack.authorEmail);}
|
|
else if(!lstrcmpA(line,"RTL")) {
|
|
_snprintf(line,sizeof(line),"%s",pszColon+1);
|
|
TrimString(line);
|
|
langPack.rtl=!lstrcmpA(line,"1");
|
|
}
|
|
else if(!lstrcmpA(line, "Locale")) {
|
|
char szBuf[20], *stopped;
|
|
|
|
TrimString(pszColon + 1);
|
|
langID = (USHORT)strtol(pszColon + 1, &stopped, 16);
|
|
langPack.localeID = MAKELCID(langID, 0);
|
|
GetLocaleInfoA(langPack.localeID, LOCALE_IDEFAULTANSICODEPAGE, szBuf, 10);
|
|
szBuf[5] = 0; // codepages have max. 5 digits
|
|
langPack.defaultANSICp = atoi(szBuf);
|
|
if (fileCp == CP_ACP)
|
|
fileCp = langPack.defaultANSICp;
|
|
}
|
|
}
|
|
|
|
//body
|
|
fseek(fp,startOfLine,SEEK_SET);
|
|
entriesAlloced=0;
|
|
while(!feof(fp)) {
|
|
if(fgets(line,sizeof(line),fp)==NULL) break;
|
|
if(IsEmpty(line) || line[0]==';' || line[0]==0) continue;
|
|
TrimStringSimple(line);
|
|
ConvertBackslashes(line, fileCp);
|
|
if(line[0]=='[' && line[lstrlenA(line)-1]==']') {
|
|
if(langPack.entryCount && langPack.entry[langPack.entryCount-1].local==NULL) {
|
|
if(langPack.entry[langPack.entryCount-1].english!=NULL) free(langPack.entry[langPack.entryCount-1].english);
|
|
langPack.entryCount--;
|
|
}
|
|
pszLine = line+1;
|
|
line[lstrlenA(line)-1]='\0';
|
|
TrimStringSimple(line);
|
|
if(++langPack.entryCount>entriesAlloced) {
|
|
entriesAlloced+=128;
|
|
langPack.entry=(struct LangPackEntry*)realloc(langPack.entry,sizeof(struct LangPackEntry)*entriesAlloced);
|
|
}
|
|
langPack.entry[langPack.entryCount-1].english=NULL;
|
|
langPack.entry[langPack.entryCount-1].englishHash=hashstr(pszLine);
|
|
langPack.entry[langPack.entryCount-1].local=NULL;
|
|
langPack.entry[langPack.entryCount-1].wlocal = NULL;
|
|
langPack.entry[langPack.entryCount-1].linePos=linePos++;
|
|
}
|
|
else if(langPack.entryCount) {
|
|
struct LangPackEntry* E = &langPack.entry[langPack.entryCount-1];
|
|
|
|
if(E->local==NULL) {
|
|
E->local = _strdup(line);
|
|
if (fileCp == CP_UTF8)
|
|
Utf8DecodeCP(E->local, langPack.defaultANSICp, NULL);
|
|
|
|
{
|
|
int iNeeded = MultiByteToWideChar(fileCp, 0, line, -1, 0, 0);
|
|
E->wlocal = (wchar_t *)malloc((iNeeded+1) * sizeof(wchar_t));
|
|
MultiByteToWideChar(fileCp, 0, line, -1, E->wlocal, iNeeded);
|
|
}
|
|
}
|
|
else {
|
|
size_t iOldLenA = strlen(E->local);
|
|
E->local = (char*)realloc(E->local, iOldLenA + strlen(line) + 2);
|
|
strcat(E->local, "\n");
|
|
strcat(E->local, line);
|
|
if (fileCp == CP_UTF8)
|
|
Utf8DecodeCP(E->local + iOldLenA + 1, langPack.defaultANSICp, NULL);
|
|
{
|
|
int iNeeded = MultiByteToWideChar(fileCp, 0, line, -1, 0, 0);
|
|
size_t iOldLen = wcslen(E->wlocal);
|
|
E->wlocal = (wchar_t*)realloc(E->wlocal, ( sizeof(wchar_t) * ( iOldLen + iNeeded + 2)));
|
|
wcscat(E->wlocal, L"\n");
|
|
MultiByteToWideChar(fileCp, 0, line, -1, E->wlocal + iOldLen+1, iNeeded);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose(fp);
|
|
|
|
qsort(langPack.entry,langPack.entryCount,sizeof(LangPackEntry),(int(*)(const void*,const void*))SortLangPackHashesProc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *LangPackTranslateString(const char *szEnglish, const int W)
|
|
{
|
|
struct LangPackEntry key,*entry;
|
|
|
|
if ( langPack.entryCount == 0 || szEnglish == NULL ) return (char*)szEnglish;
|
|
|
|
key.englishHash = W ? hashstrW(szEnglish) : hashstr(szEnglish);
|
|
|
|
entry=(struct LangPackEntry*)bsearch(&key,langPack.entry,langPack.entryCount,sizeof(struct LangPackEntry),(int(*)(const void*,const void*))SortLangPackHashesProc2);
|
|
if(entry==NULL) return (char*)szEnglish;
|
|
while(entry>langPack.entry)
|
|
{
|
|
entry--;
|
|
if(entry->englishHash!=key.englishHash) {
|
|
entry++;
|
|
return W ? (char *)entry->wlocal : entry->local;
|
|
}
|
|
}
|
|
return W ? (char *)entry->wlocal : entry->local;
|
|
}
|
|
|
|
#if defined( _UNICODE )
|
|
#define FLAGS 1
|
|
#else
|
|
#define FLAGS 0
|
|
#endif
|
|
|
|
static void TranslateWindow( HWND hwnd )
|
|
{
|
|
TCHAR title[2048];
|
|
GetWindowText(hwnd, title, SIZEOF( title ));
|
|
{
|
|
TCHAR* result = ( TCHAR* )LangPackTranslateString(( char* )title, FLAGS );
|
|
if ( result != title )
|
|
SetWindowText(hwnd, result );
|
|
}
|
|
}
|
|
|
|
static BOOL CALLBACK TranslateDialogEnumProc(HWND hwnd,LPARAM lParam)
|
|
{
|
|
TCHAR szClass[32];
|
|
int id = GetDlgCtrlID( hwnd );
|
|
|
|
GetClassName(hwnd,szClass,SIZEOF(szClass));
|
|
|
|
if(!lstrcmpi(szClass,_T("static")) || !lstrcmpi(szClass,_T("hyperlink")) || !lstrcmpi(szClass,_T("button")) || !lstrcmpi(szClass,_T("MButtonClass")))
|
|
TranslateWindow(hwnd);
|
|
else if(!lstrcmpi(szClass,_T("edit"))) {
|
|
if ( GetWindowLongPtr(hwnd,GWL_STYLE)&ES_READONLY)
|
|
TranslateWindow(hwnd);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
int TranslateDialog( HWND hwndDlg )
|
|
{
|
|
TranslateWindow( hwndDlg );
|
|
EnumChildWindows( hwndDlg,TranslateDialogEnumProc,0);
|
|
return 0;
|
|
}
|
|
|
|
void TranslateMenu( HMENU hmenu )
|
|
{
|
|
TCHAR title[2048];
|
|
int count = GetMenuItemCount( hmenu );
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (GetMenuString( hmenu, i, title, 2048, MF_BYPOSITION ))
|
|
{
|
|
wchar_t * p = wcsstr(title ,_T("\t"));
|
|
TCHAR translated[2048] = _T("");
|
|
if (p!=NULL) {
|
|
*p = 0;
|
|
}
|
|
StringCchCat(translated,2048,(LPTSTR)LangPackTranslateString((char*)title, FLAGS));
|
|
if (p!=NULL) {
|
|
*p = '\t';
|
|
StringCchCat(translated,2048,p);
|
|
}
|
|
ModifyMenu(
|
|
hmenu,
|
|
i,
|
|
MF_BYPOSITION | MF_STRING,
|
|
GetMenuItemID( hmenu, i ),
|
|
translated
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void LoadLangPackModule(void)
|
|
{
|
|
HANDLE hFind;
|
|
TCHAR szSearch[MAX_PATH], *str2, szLangPack[MAX_PATH];
|
|
WIN32_FIND_DATA fd;
|
|
langPack.rtl = 0;
|
|
GetModuleFileName(GetModuleHandle(NULL),szSearch,MAX_PATH);
|
|
str2 = _tcsrchr(szSearch, '\\');
|
|
if (str2) *str2 = 0; else str2 = szSearch;
|
|
_tcscat(szSearch, _T("\\langpack_*.txt"));
|
|
hFind = FindFirstFile(szSearch, &fd);
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
FindClose(hFind);
|
|
|
|
_tcscpy(str2 + 1, fd.cFileName);
|
|
_tcscpy(szLangPack, szSearch);
|
|
LoadLangPack(szLangPack);
|
|
}
|
|
}
|
|
|
|
void UnloadLangPackModule(void)
|
|
{
|
|
for (int i = 0; i < langPack.entryCount; i++) {
|
|
free(langPack.entry[i].english);
|
|
free(langPack.entry[i].local);
|
|
free(langPack.entry[i].wlocal);
|
|
}
|
|
if (langPack.entryCount) {
|
|
free(langPack.entry);
|
|
langPack.entry=0;
|
|
langPack.entryCount=0;
|
|
}
|
|
langPack.rtl = 0;
|
|
}
|