mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
617 lines
18 KiB
617 lines
18 KiB
#include <windows.h>
|
|
#include <immdev.h>
|
|
#include "imeattr.h"
|
|
#include "imedefs.h"
|
|
#include "imerc.h"
|
|
#include "uniime.h"
|
|
|
|
BOOL IsBig5Character( WCHAR wChar );
|
|
|
|
/**********************************************************************/
|
|
/* AddPhraseString() */
|
|
/**********************************************************************/
|
|
DWORD PASCAL AddPhraseString(
|
|
LPIMEL lpImeL,
|
|
LPCANDIDATELIST lpCandList,
|
|
DWORD dwPhraseOffset,
|
|
DWORD dwPhraseNextOffset,
|
|
DWORD dwStartLen,
|
|
DWORD dwEndLen,
|
|
DWORD dwAddCandLimit,
|
|
BOOL fConvertCP)
|
|
{
|
|
HANDLE hLCPhraseTbl;
|
|
LPWSTR lpLCPhraseTbl;
|
|
LPWSTR lpStart, lpEnd;
|
|
int iBytes;
|
|
DWORD dwMaxCand;
|
|
DWORD dwOffset;
|
|
BOOL bIsNotBig5Char, bIsBig5OnlyMode;
|
|
|
|
// put the strings into candidate list
|
|
hLCPhraseTbl = OpenFileMapping(FILE_MAP_READ, FALSE,
|
|
sImeG.szTblFile[1]);
|
|
if (!hLCPhraseTbl) {
|
|
return (0);
|
|
}
|
|
|
|
lpLCPhraseTbl = (LPWSTR)MapViewOfFile(hLCPhraseTbl, FILE_MAP_READ,
|
|
0, 0, 0);
|
|
if (!lpLCPhraseTbl) {
|
|
dwOffset = 0;
|
|
goto AddPhraseStringUnmap;
|
|
}
|
|
|
|
if (lpImeL->fdwModeConfig & MODE_CONFIG_BIG5ONLY)
|
|
bIsBig5OnlyMode = TRUE;
|
|
else
|
|
bIsBig5OnlyMode = FALSE;
|
|
|
|
lpStart = lpLCPhraseTbl + dwPhraseOffset;
|
|
lpEnd = lpLCPhraseTbl + dwPhraseNextOffset;
|
|
|
|
if (!lpCandList) {
|
|
// ask how many candidate list strings
|
|
dwOffset = 0;
|
|
|
|
for (lpStart; lpStart < lpEnd;) {
|
|
|
|
bIsNotBig5Char = FALSE;
|
|
|
|
for (; lpStart < lpEnd; lpStart++) {
|
|
WORD uCode;
|
|
|
|
uCode = *lpStart;
|
|
|
|
// one string finished
|
|
#ifdef UNICODE
|
|
if (!uCode) {
|
|
#else
|
|
if (!(uCode & 0x8000)) {
|
|
#endif
|
|
lpStart++;
|
|
break;
|
|
}
|
|
|
|
if ( bIsBig5OnlyMode ) {
|
|
|
|
if ( IsBig5Character((WCHAR)uCode) == FALSE )
|
|
bIsNotBig5Char = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
// if it is in Big5 Only Mode, and there is at least one char which is not
|
|
// in Big5 charset, we don't count this string
|
|
|
|
if ( bIsBig5OnlyMode && bIsNotBig5Char )
|
|
continue;
|
|
|
|
// string count plus 1
|
|
dwOffset++;
|
|
}
|
|
|
|
goto AddPhraseStringUnmap;
|
|
}
|
|
|
|
// the offset of dwOffset[0]
|
|
dwOffset = (DWORD)((LPBYTE)&lpCandList->dwOffset[0] - (LPBYTE)lpCandList);
|
|
|
|
if (lpCandList->dwSize < dwOffset) {
|
|
return (0);
|
|
}
|
|
|
|
// how many bytes of dwOffset[]
|
|
iBytes = lpCandList->dwOffset[0] - dwOffset;
|
|
|
|
// maybe the size is even smaller than it
|
|
for (dwMaxCand = 1; dwMaxCand < lpCandList->dwCount; dwMaxCand++) {
|
|
if ((int)(lpCandList->dwOffset[dwMaxCand] - dwOffset) < iBytes) {
|
|
iBytes = (int)(lpCandList->dwOffset[dwMaxCand] - dwOffset);
|
|
}
|
|
}
|
|
|
|
if (iBytes <= 0) {
|
|
return (0);
|
|
}
|
|
|
|
dwMaxCand = (DWORD)iBytes / sizeof(DWORD);
|
|
|
|
if (dwAddCandLimit < dwMaxCand) {
|
|
dwMaxCand = dwAddCandLimit;
|
|
}
|
|
|
|
if (lpCandList->dwCount >= dwMaxCand) {
|
|
// Grow memory here and do something,
|
|
// if you still want to process it.
|
|
return (0);
|
|
}
|
|
|
|
dwOffset = lpCandList->dwOffset[lpCandList->dwCount];
|
|
|
|
for (lpStart; lpStart < lpEnd;) {
|
|
BOOL fStrEnd;
|
|
DWORD dwStrLen, dwCharLen, dwStrByteLen, dwCharByteLen;
|
|
|
|
fStrEnd = FALSE;
|
|
bIsNotBig5Char = FALSE;
|
|
|
|
// get the whole string
|
|
dwCharByteLen = sizeof(WCHAR);
|
|
dwCharLen = sizeof(WCHAR) / sizeof(TCHAR);
|
|
|
|
for (dwStrLen = dwStrByteLen = 0; !fStrEnd && (lpStart < lpEnd);
|
|
lpStart++, dwStrLen+= dwCharLen, dwStrByteLen += dwCharByteLen) {
|
|
WORD uCode;
|
|
|
|
uCode = *lpStart;
|
|
|
|
// one string finished
|
|
#ifdef UNICODE
|
|
if (!uCode) {
|
|
#else
|
|
if (!(uCode & 0x8000)) {
|
|
#endif
|
|
fStrEnd = TRUE;
|
|
#ifdef UNICODE
|
|
lpStart++;
|
|
break;
|
|
#else
|
|
uCode |= 0x8000;
|
|
#endif
|
|
}
|
|
|
|
// if it is Big5Only Mode, we need to check if this char is in Big5 charset
|
|
|
|
if ( bIsBig5OnlyMode ) {
|
|
|
|
if ( !IsBig5Character((WCHAR)uCode) )
|
|
bIsNotBig5Char = TRUE;
|
|
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
if (fConvertCP) {
|
|
CHAR szCode[4];
|
|
|
|
dwCharLen = dwCharByteLen = WideCharToMultiByte(
|
|
sImeG.uAnsiCodePage, WC_COMPOSITECHECK,
|
|
(LPCWSTR)&uCode, 1, szCode, sizeof(szCode), NULL, NULL);
|
|
|
|
// because this BIG5 code, convert to BIG5 string
|
|
if (dwCharByteLen >= 2) {
|
|
uCode = (BYTE)szCode[0] | ((UINT)(BYTE)szCode[1] << 8);
|
|
} else {
|
|
uCode = (UINT)szCode[0];
|
|
}
|
|
}
|
|
#else
|
|
// swap lead & second byte (as a string), UNICODE don't need it
|
|
uCode = HIBYTE(uCode) | (LOBYTE(uCode) << 8);
|
|
#endif
|
|
|
|
if ((dwOffset + dwStrByteLen + dwCharByteLen) >=
|
|
lpCandList->dwSize) {
|
|
goto AddPhraseStringClose;
|
|
}
|
|
|
|
// add this char into candidate list
|
|
#ifdef UNICODE
|
|
if (dwCharByteLen == sizeof(WCHAR)) {
|
|
*(LPWSTR)((LPBYTE)lpCandList + dwOffset + dwStrByteLen) =
|
|
(WCHAR)uCode;
|
|
} else {
|
|
*(LPSTR)((LPBYTE)lpCandList + dwOffset + dwStrByteLen) =
|
|
(CHAR)uCode;
|
|
}
|
|
#else
|
|
*(LPWSTR)((LPBYTE)lpCandList + dwOffset + dwStrByteLen) =
|
|
(WCHAR)uCode;
|
|
#endif
|
|
}
|
|
|
|
if (dwStrLen < dwStartLen) {
|
|
// the found string too short
|
|
continue;
|
|
} else if (dwStrLen >= dwEndLen) {
|
|
// the found string too long
|
|
continue;
|
|
} else {
|
|
}
|
|
|
|
// if it is in Big5 Only Mode, and there is at least one char which is not in Big5
|
|
// charset, we just ingore this string, do not put it into the candidate list
|
|
|
|
if ( bIsBig5OnlyMode && bIsNotBig5Char ) {
|
|
|
|
bIsNotBig5Char = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if ((dwOffset + dwStrByteLen + sizeof(TCHAR)) >= lpCandList->dwSize) {
|
|
goto AddPhraseStringClose;
|
|
}
|
|
|
|
// null terminator
|
|
*(LPTSTR)((LPBYTE)lpCandList + dwOffset + dwStrByteLen) = TEXT('\0');
|
|
dwOffset += (dwStrByteLen + sizeof(TCHAR));
|
|
|
|
// add one string into candidate list
|
|
lpCandList->dwCount++;
|
|
|
|
if (lpCandList->dwCount >= dwMaxCand) {
|
|
// Grow memory here and do something,
|
|
// if you still want to process it.
|
|
break;
|
|
}
|
|
|
|
// string length plus size of the null terminator
|
|
lpCandList->dwOffset[lpCandList->dwCount] = dwOffset;
|
|
}
|
|
|
|
AddPhraseStringUnmap:
|
|
UnmapViewOfFile(lpLCPhraseTbl);
|
|
AddPhraseStringClose:
|
|
CloseHandle(hLCPhraseTbl);
|
|
|
|
return (dwOffset);
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* UniSearchPhrasePrediction() */
|
|
/* Description: */
|
|
/* file format can be changed in different version for */
|
|
/* performance consideration, ISVs should not assume its format */
|
|
/* and serach these files by themselves */
|
|
/**********************************************************************/
|
|
DWORD WINAPI UniSearchPhrasePrediction(
|
|
LPIMEL lpImeL,
|
|
UINT uCodePage,
|
|
LPCTSTR lpszStr,
|
|
DWORD dwStrLen,
|
|
LPCTSTR lpszReadStr, // Phonetic reading string
|
|
DWORD dwReadStrLen,
|
|
DWORD dwStartLen, // find the string length >= this value
|
|
DWORD dwEndLen, // find the string length < this value
|
|
DWORD dwAddCandLimit,
|
|
LPCANDIDATELIST lpCandList)
|
|
{
|
|
UINT uCode;
|
|
HANDLE hLCPtrTbl;
|
|
LPWORD lpLCPtrTbl;
|
|
int iLo, iHi, iMid;
|
|
BOOL fFound, fConvertCP;
|
|
DWORD dwPhraseOffset, dwPhraseNextOffset;
|
|
|
|
if (uCodePage == NATIVE_CP) {
|
|
fConvertCP = FALSE;
|
|
#ifdef UNICODE
|
|
} else if (uCodePage == sImeG.uAnsiCodePage) {
|
|
fConvertCP = TRUE;
|
|
#endif
|
|
} else {
|
|
return (0);
|
|
}
|
|
|
|
if (dwStrLen != sizeof(WCHAR) / sizeof(TCHAR)) {
|
|
return (0);
|
|
}
|
|
|
|
if (dwStartLen >= dwEndLen) {
|
|
return (0);
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
uCode = lpszStr[0];
|
|
#else
|
|
// swap lead byte & second byte, UNICODE don't need it
|
|
uCode = (BYTE)lpszStr[1];
|
|
*((LPBYTE)&uCode + 1) = (BYTE)lpszStr[0];
|
|
#endif
|
|
|
|
iLo = 0;
|
|
#ifdef UNICODE
|
|
iHi = sImeG.uTblSize[0] / 6;
|
|
#else
|
|
iHi = sImeG.uTblSize[0] / 4;
|
|
#endif
|
|
iMid = (iHi + iLo) /2;
|
|
|
|
fFound = FALSE;
|
|
|
|
// LCPTR.TBL
|
|
hLCPtrTbl = OpenFileMapping(FILE_MAP_READ, FALSE, sImeG.szTblFile[0]);
|
|
if (!hLCPtrTbl) {
|
|
return (0);
|
|
}
|
|
|
|
lpLCPtrTbl = MapViewOfFile(hLCPtrTbl, FILE_MAP_READ, 0, 0, 0);
|
|
if (!lpLCPtrTbl) {
|
|
goto SrchPhrPredictClose;
|
|
}
|
|
|
|
// binary search on phrase table,
|
|
// one is multiple word phrase and the other is the two word phrase
|
|
for (; iLo <= iHi;) {
|
|
LPWORD lpCurr;
|
|
|
|
#ifdef UNICODE
|
|
lpCurr = lpLCPtrTbl + 3 * iMid;
|
|
#else
|
|
lpCurr = lpLCPtrTbl + 2 * iMid;
|
|
#endif
|
|
|
|
if (uCode > *lpCurr) {
|
|
iLo = iMid + 1;
|
|
} else if (uCode < *lpCurr) {
|
|
iHi = iMid - 1;
|
|
} else {
|
|
fFound = TRUE;
|
|
// use it on TAB key
|
|
#ifdef UNICODE
|
|
dwPhraseOffset = *(LPUNADWORD)(lpCurr + 1);
|
|
dwPhraseNextOffset = *(LPUNADWORD)(lpCurr + 1 + 3);
|
|
#else
|
|
dwPhraseOffset = *(lpCurr + 1);
|
|
dwPhraseNextOffset = *(lpCurr + 1 + 2);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
iMid = (iHi + iLo) /2;
|
|
}
|
|
|
|
UnmapViewOfFile(lpLCPtrTbl);
|
|
|
|
SrchPhrPredictClose:
|
|
CloseHandle(hLCPtrTbl);
|
|
|
|
if (!fFound) {
|
|
return (0);
|
|
}
|
|
|
|
// phrase string
|
|
return AddPhraseString(lpImeL,lpCandList, dwPhraseOffset, dwPhraseNextOffset,
|
|
dwStartLen, dwEndLen, dwAddCandLimit, fConvertCP);
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* UniSearchPhrasePredictionStub() */
|
|
/* Description: */
|
|
/* file format can be changed in different version for */
|
|
/* performance consideration, ISVs should not assume its format */
|
|
/* and serach these files by themselves */
|
|
/**********************************************************************/
|
|
DWORD WINAPI UniSearchPhrasePredictionStub(
|
|
LPIMEL lpImeL,
|
|
UINT uCodePage,
|
|
LPCSTUBSTR lpszStr,
|
|
DWORD dwStrLen,
|
|
LPCSTUBSTR lpszReadStr, // Phonetic reading string
|
|
DWORD dwReadStrLen,
|
|
DWORD dwStartLen, // find the string length >= this value
|
|
DWORD dwEndLen, // find the string length < this value
|
|
DWORD dwAddCandLimit,
|
|
LPCANDIDATELIST lpCandList)
|
|
{
|
|
#ifdef UNICODE
|
|
LPTSTR lpszWideStr, lpszWideReadStr;
|
|
DWORD dwWideStrLen, dwWideReadStrLen;
|
|
DWORD dwWideStartLen, dwWideEndLen;
|
|
DWORD dwWideAddCandList, dwRet;
|
|
LPCANDIDATELIST lpWideCandList;
|
|
LPBYTE lpbBuf;
|
|
|
|
if (uCodePage != sImeG.uAnsiCodePage) {
|
|
return (0);
|
|
}
|
|
|
|
dwRet = dwStrLen * sizeof(WCHAR) + dwReadStrLen * sizeof(WCHAR);
|
|
|
|
lpbBuf = (LPBYTE)GlobalAlloc(GPTR, dwRet);
|
|
if ( lpbBuf == NULL )
|
|
return 0;
|
|
|
|
if (lpszStr) {
|
|
lpszWideStr = (LPTSTR)lpbBuf;
|
|
|
|
dwWideStrLen = MultiByteToWideChar(sImeG.uAnsiCodePage,
|
|
MB_PRECOMPOSED, lpszStr, dwStrLen,
|
|
lpszWideStr, dwStrLen);
|
|
} else {
|
|
lpszWideStr = NULL;
|
|
dwWideStrLen = 0;
|
|
}
|
|
|
|
if (lpszReadStr) {
|
|
lpszWideReadStr = (LPTSTR)(lpbBuf + dwStrLen * sizeof(WCHAR));
|
|
|
|
dwWideReadStrLen = MultiByteToWideChar(sImeG.uAnsiCodePage,
|
|
MB_PRECOMPOSED, lpszReadStr, dwReadStrLen,
|
|
lpszWideReadStr, dwReadStrLen);
|
|
} else {
|
|
lpszWideReadStr = NULL;
|
|
dwWideReadStrLen = 0;
|
|
}
|
|
|
|
dwRet = UniSearchPhrasePrediction(lpImeL,uCodePage, lpszWideStr, dwWideStrLen,
|
|
lpszWideReadStr, dwWideReadStrLen, dwStartLen, dwEndLen,
|
|
dwAddCandLimit, lpCandList);
|
|
|
|
// now, start W to A conversion and fliter the real limit here
|
|
GlobalFree((HGLOBAL)lpbBuf);
|
|
|
|
return (dwRet);
|
|
#else
|
|
return (0);
|
|
#endif
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* MemoryLack() */
|
|
/**********************************************************************/
|
|
void PASCAL MemoryLack(
|
|
DWORD fdwErrMsg)
|
|
{
|
|
TCHAR szErrMsg[64];
|
|
TCHAR szIMEName[16];
|
|
|
|
if (sImeG.fdwErrMsg & fdwErrMsg) {
|
|
// message already prompted
|
|
return;
|
|
}
|
|
|
|
LoadString(hInst, IDS_MEM_LACK_FAIL, szErrMsg, sizeof(szErrMsg)/sizeof(TCHAR));
|
|
LoadString(hInst, IDS_IMENAME, szIMEName, sizeof(szIMEName)/sizeof(TCHAR) );
|
|
|
|
sImeG.fdwErrMsg |= fdwErrMsg;
|
|
MessageBeep((UINT)-1);
|
|
MessageBox((HWND)NULL, szErrMsg, szIMEName,
|
|
MB_OK|MB_ICONHAND|MB_TASKMODAL|MB_TOPMOST);
|
|
|
|
return;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* LoadOneGlobalTable() */
|
|
/* Description: */
|
|
/* memory handle & size of .TBL file will be assigned to */
|
|
/* sImeG */
|
|
/* Eeturn Value: */
|
|
/* length of directory of the .TBL file */
|
|
/**********************************************************************/
|
|
UINT PASCAL LoadOneGlobalTable( // load one of table file
|
|
LPTSTR szTable, // file name of .TBL
|
|
UINT uIndex, // the index of array to store memory handle
|
|
UINT uLen, // length of the directory
|
|
LPTSTR szPath) // buffer for directory
|
|
{
|
|
HANDLE hTblFile;
|
|
HGLOBAL hMap;
|
|
TCHAR szFullPathFile[MAX_PATH];
|
|
PSECURITY_ATTRIBUTES psa;
|
|
|
|
CopyMemory(szFullPathFile, szPath, uLen * sizeof(TCHAR));
|
|
|
|
psa = CreateSecurityAttributes();
|
|
|
|
if (uLen) {
|
|
CopyMemory(&szFullPathFile[uLen], szTable, sizeof(sImeG.szTblFile[0]));
|
|
hTblFile = CreateFile(szFullPathFile, GENERIC_READ,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
psa, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
|
|
} else {
|
|
// try system directory
|
|
uLen = GetSystemDirectory(szFullPathFile, MAX_PATH);
|
|
if (szFullPathFile[uLen - 1] != TEXT('\\')) { // consider N:\ ;
|
|
szFullPathFile[uLen++] = TEXT('\\');
|
|
}
|
|
|
|
CopyMemory(&szFullPathFile[uLen], szTable, sizeof(sImeG.szTblFile[0]));
|
|
hTblFile = CreateFile(szFullPathFile, GENERIC_READ,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
psa, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
|
|
|
|
if (hTblFile != INVALID_HANDLE_VALUE) {
|
|
goto CopyDicPath;
|
|
}
|
|
|
|
// if the work station version, SHARE_WRITE will fail
|
|
hTblFile = CreateFile(szFullPathFile, GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
psa, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
|
|
|
|
CopyDicPath:
|
|
if (hTblFile != INVALID_HANDLE_VALUE) {
|
|
CopyMemory(sImeG.szPhrasePath, szFullPathFile, uLen * sizeof(TCHAR));
|
|
sImeG.uPathLen = uLen;
|
|
goto OpenDicFile;
|
|
}
|
|
}
|
|
|
|
OpenDicFile:
|
|
// can not find the table file
|
|
if (hTblFile != INVALID_HANDLE_VALUE) { // OK
|
|
} else if (sImeG.fdwErrMsg & (ERRMSG_LOAD_0 << uIndex)) {
|
|
// already prompt error message before, no more
|
|
FreeSecurityAttributes(psa);
|
|
return (0);
|
|
} else { // prompt error message
|
|
TCHAR szIMEName[64];
|
|
TCHAR szErrMsg[2 * MAX_PATH];
|
|
|
|
// temp use szIMEName as format string buffer of error message
|
|
LoadString(hInst, IDS_FILE_OPEN_FAIL, szIMEName, sizeof(szIMEName)/sizeof(TCHAR));
|
|
wsprintf(szErrMsg, szIMEName, szTable);
|
|
|
|
LoadString(hInst, IDS_IMENAME, szIMEName, sizeof(szIMEName)/sizeof(TCHAR));
|
|
sImeG.fdwErrMsg |= ERRMSG_LOAD_0 << uIndex;
|
|
MessageBeep((UINT)-1);
|
|
MessageBox((HWND)NULL, szErrMsg, szIMEName,
|
|
MB_OK|MB_ICONHAND|MB_TASKMODAL|MB_TOPMOST);
|
|
FreeSecurityAttributes(psa);
|
|
return (0);
|
|
}
|
|
|
|
sImeG.fdwErrMsg &= ~(ERRMSG_LOAD_0 << uIndex);
|
|
|
|
// create file mapping for IME tables
|
|
hMap = CreateFileMapping((HANDLE)hTblFile, psa, PAGE_READONLY,
|
|
0, 0, szTable);
|
|
|
|
if (!hMap) {
|
|
MemoryLack(ERRMSG_MEM_0 << uIndex);
|
|
CloseHandle(hTblFile);
|
|
FreeSecurityAttributes(psa);
|
|
return(0);
|
|
}
|
|
|
|
sImeG.fdwErrMsg &= ~(ERRMSG_MEM_0 << uIndex);
|
|
|
|
sInstG.hMapTbl[uIndex] = hMap;
|
|
|
|
// get file length
|
|
sImeG.uTblSize[uIndex] = GetFileSize(hTblFile, (LPDWORD)NULL);
|
|
|
|
CloseHandle(hTblFile);
|
|
FreeSecurityAttributes(psa);
|
|
|
|
return (uLen);
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* LoadPhraseTable() */
|
|
/* Return Value: */
|
|
/* TRUE - successful, FALSE - failure */
|
|
/**********************************************************************/
|
|
BOOL PASCAL LoadPhraseTable( // load the phrase tables
|
|
UINT uLen, // length of the directory
|
|
LPTSTR szPath) // buffer for directory
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_PHRASE_TABLES; i++) {
|
|
if (!*sImeG.szTblFile[i]) {
|
|
} else if (sInstG.hMapTbl[i]) { // already loaded
|
|
} else if (uLen = LoadOneGlobalTable(sImeG.szTblFile[i], i,
|
|
uLen, szPath)) {
|
|
} else {
|
|
int j;
|
|
|
|
for (j = 0; j < i; j++) {
|
|
if (sInstG.hMapTbl[j]) {
|
|
CloseHandle(sInstG.hMapTbl[j]);
|
|
sInstG.hMapTbl[j] = (HANDLE)NULL;
|
|
}
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|