/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2011 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "dbtool.h" struct LangPackEntry { unsigned linePos; DWORD englishHash; char *english; //not currently used, the hash does everything char *local; wchar_t *wlocal; }; struct LangPackStruct { TCHAR filename[MAX_PATH]; char language[64]; char lastModifiedUsing[64]; char authors[256]; char authorEmail[128]; struct LangPackEntry *entry; int entryCount; LCID localeID; UINT defaultANSICp; } static 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->englishHashenglishHash) 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->linePoslinePos) return -1; if(arg1->linePos>arg2->linePos) return 1; return 0; } static int SortLangPackHashesProc2(struct LangPackEntry *arg1,struct LangPackEntry *arg2) { if(arg1->englishHashenglishHash) 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,"Miranda Language Pack Version 1")) {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, "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 LoadLangPackModule(void) { HANDLE hFind; TCHAR szSearch[MAX_PATH], *str2, szLangPack[MAX_PATH]; WIN32_FIND_DATA fd; 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; } }