1 #ifdef WIN32
2 #define WIN32_LEAN_AND_MEAN
3 #include <windows.h>
4 #else
5 #include "wintypes.h"
6 #endif
7 #include <winnls.h>
8 
9 #include <stdio.h>
10 #include <assert.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include "nls.h"
15 #include "nlstables.h"
16 
17 #ifdef WIN32
18 #define NlsStrLenW lstrlenW             // Hack
19 #else
20 #define NlsStrLenW wcslen
21 #endif
22 
23 
24 //
25 //  Invalid weight value.
26 //
27 #define MAP_INVALID_UW  0xffff
28 
29 #define NUM_BYTES_UW		2
30 #define NUM_BYTES_DW		1
31 #define NUM_BYTES_CW		1
32 #define NUM_BYTES_XW		4
33 
34 #define NORM_DROP_CW         (NORM_IGNORECASE | NORM_IGNOREWIDTH)
35 
36 #define EXTRA_WEIGHT_POS(WtNum)        (*(pPosXW + (WtNum * WeightLen)))
37 
38 #define SPECIAL_CASE_HANDLER( SM,                                           \
39                               pWeight,                                      \
40                               pSortkey,                                     \
41                               pExpand,                                      \
42                               Position,                                     \
43                               fStringSort,                                  \
44                               fIgnoreSymbols,                               \
45                               pCur,                                         \
46                               pBegin )                                      \
47 {                                                                           \
48     PSORTKEY pExpWt;              /* weight of 1 expansion char */          \
49     BYTE AW;                      /* alphanumeric weight */                 \
50     BYTE XW;                      /* case weight value with extra bits */   \
51     DWORD PrevWt;                 /* previous weight */                     \
52     BYTE PrevSM;                  /* previous script member */              \
53     BYTE PrevAW;                  /* previuos alphanumeric weight */        \
54     BYTE PrevCW;                  /* previuos case weight */                \
55     LPWSTR pPrev;                 /* ptr to previous char */                \
56                                                                             \
57                                                                             \
58     switch (SM)                                                             \
59     {                                                                       \
60         case ( UNSORTABLE ) :                                               \
61         {                                                                   \
62             /*                                                              \
63              *  Character is unsortable, so skip it.                        \
64              */                                                             \
65             break;                                                          \
66         }                                                                   \
67                                                                             \
68         case ( NONSPACE_MARK ) :                                            \
69         {                                                                   \
70             /*                                                              \
71              *  Character is a nonspace mark, so only store                 \
72              *  the diacritic weight.                                       \
73              */                                                             \
74             if (pPosDW > pDW)                                               \
75             {                                                               \
76                 (*(pPosDW - 1)) += GET_DIACRITIC(pWeight);                  \
77             }                                                               \
78             else                                                            \
79             {                                                               \
80                 *pPosDW = GET_DIACRITIC(pWeight);                           \
81                 pPosDW++;                                                   \
82             }                                                               \
83                                                                             \
84             break;                                                          \
85         }                                                                   \
86                                                                             \
87         case ( EXPANSION ) :                                                \
88         {                                                                   \
89             /*                                                              \
90              *  Expansion character - one character has 2                   \
91              *  different weights.  Store each weight separately.           \
92              */                                                             \
93             pExpWt = &(pSortkey[(pExpand[GET_EXPAND_INDEX(pWeight)]).UCP1]); \
94             *pPosUW = GET_UNICODE(pExpWt);                                  \
95             *pPosDW = GET_DIACRITIC(pExpWt);                                \
96             *pPosCW = GET_CASE(pExpWt) & CaseMask;                          \
97             pPosUW++;                                                       \
98             pPosDW++;                                                       \
99             pPosCW++;                                                       \
100                                                                             \
101             pExpWt = &(pSortkey[(pExpand[GET_EXPAND_INDEX(pWeight)]).UCP2]); \
102             *pPosUW = GET_UNICODE(pExpWt);                                  \
103             *pPosDW = GET_DIACRITIC(pExpWt);                                \
104             *pPosCW = GET_CASE(pExpWt) & CaseMask;                          \
105             pPosUW++;                                                       \
106             pPosDW++;                                                       \
107             pPosCW++;                                                       \
108                                                                             \
109             break;                                                          \
110         }                                                                   \
111                                                                             \
112         case ( PUNCTUATION ) :                                              \
113         {                                                                   \
114             if (!fStringSort)                                               \
115             {                                                               \
116                 /*                                                          \
117                  *  Word Sort Method.                                       \
118                  *                                                          \
119                  *  Character is punctuation, so only store the special     \
120                  *  weight.                                                 \
121                  */                                                         \
122                 *((LPBYTE)pPosSW)       = HIBYTE(GET_POSITION_SW(Position)); \
123                 *(((LPBYTE)pPosSW) + 1) = LOBYTE(GET_POSITION_SW(Position)); \
124                 pPosSW++;                                                   \
125                 *pPosSW = GET_SPECIAL_WEIGHT(pWeight);                      \
126                 pPosSW++;                                                   \
127                                                                             \
128                 break;                                                      \
129             }                                                               \
130                                                                             \
131             /*                                                              \
132              *  If using STRING sort method, treat punctuation the same     \
133              *  as symbol.  So, FALL THROUGH to the symbol cases.           \
134              */                                                             \
135         }                                                                   \
136                                                                             \
137         case ( SYMBOL_1 ) :                                                 \
138         case ( SYMBOL_2 ) :                                                 \
139         case ( SYMBOL_3 ) :                                                 \
140         case ( SYMBOL_4 ) :                                                 \
141         case ( SYMBOL_5 ) :                                                 \
142         {                                                                   \
143             /*                                                              \
144              *  Character is a symbol.                                      \
145              *  Store the Unicode weights ONLY if the NORM_IGNORESYMBOLS    \
146              *  flag is NOT set.                                            \
147              */                                                             \
148             if (!fIgnoreSymbols)                                            \
149             {                                                               \
150                 *pPosUW = GET_UNICODE(pWeight);                             \
151                 *pPosDW = GET_DIACRITIC(pWeight);                           \
152                 *pPosCW = GET_CASE(pWeight) & CaseMask;                     \
153                 pPosUW++;                                                   \
154                 pPosDW++;                                                   \
155                 pPosCW++;                                                   \
156             }                                                               \
157                                                                             \
158             break;                                                          \
159         }                                                                   \
160                                                                             \
161         case ( FAREAST_SPECIAL ) :                                          \
162         {                                                                   \
163             /*                                                              \
164              *  Get the alphanumeric weight and the case weight of the      \
165              *  current code point.                                         \
166              */                                                             \
167             AW = GET_ALPHA_NUMERIC(pWeight);                                \
168             XW = (GET_CASE(pWeight) & CaseMask) | CASE_XW_MASK;             \
169                                                                             \
170             /*                                                              \
171              *  Special case Repeat and Cho-On.                             \
172              *    AW = 0  =>  Repeat                                        \
173              *    AW = 1  =>  Cho-On                                        \
174              *    AW = 2+ =>  Kana                                          \
175              */                                                             \
176             if (AW <= MAX_SPECIAL_AW)                                       \
177             {                                                               \
178                 /*                                                          \
179                  *  If the script member of the previous character is       \
180                  *  invalid, then give the special character an             \
181                  *  invalid weight (highest possible weight) so that it     \
182                  *  will sort AFTER everything else.                        \
183                  */                                                         \
184                 pPrev = pCur - 1;                                           \
185                 *pPosUW = MAP_INVALID_UW;                                   \
186                 while (pPrev >= pBegin)                                     \
187                 {                                                           \
188                     PrevWt = GET_DWORD_WEIGHT(pHashN, *pPrev);              \
189                     PrevSM = GET_SCRIPT_MEMBER(&PrevWt);                    \
190                     if (PrevSM < FAREAST_SPECIAL)                           \
191                     {                                                       \
192                         if (PrevSM != EXPANSION)                            \
193                         {                                                   \
194                             /*                                              \
195                              *  UNSORTABLE or NONSPACE_MARK.                \
196                              *                                              \
197                              *  Just ignore these, since we only care       \
198                              *  about the previous UW value.                \
199                              */                                             \
200                             pPrev--;                                        \
201                             continue;                                       \
202                         }                                                   \
203                     }                                                       \
204                     else if (PrevSM == FAREAST_SPECIAL)                     \
205                     {                                                       \
206                         PrevAW = GET_ALPHA_NUMERIC(&PrevWt);                \
207                         if (PrevAW <= MAX_SPECIAL_AW)                       \
208                         {                                                   \
209                             /*                                              \
210                              *  Handle case where two special chars follow  \
211                              *  each other.  Keep going back in the string. \
212                              */                                             \
213                             pPrev--;                                        \
214                             continue;                                       \
215                         }                                                   \
216                                                                             \
217                         *pPosUW = MAKE_UNICODE_WT(KANA, PrevAW);            \
218                                                                             \
219                         /*                                                  \
220                          *  Only build weights 4, 5, 6, and 7 if the        \
221                          *  previous character is KANA.                     \
222                          *                                                  \
223                          *  Always:                                         \
224                          *    4W = previous CW  &  ISOLATE_SMALL            \
225                          *    6W = previous CW  &  ISOLATE_KANA             \
226                          *                                                  \
227                          */                                                 \
228                         PrevCW = (GET_CASE(&PrevWt) & CaseMask) |           \
229                                  CASE_XW_MASK;                              \
230                         EXTRA_WEIGHT_POS(0) = PrevCW & ISOLATE_SMALL;       \
231                         EXTRA_WEIGHT_POS(2) = PrevCW & ISOLATE_KANA;        \
232                                                                             \
233                         if (AW == AW_REPEAT)                                \
234                         {                                                   \
235                             /*                                              \
236                              *  Repeat:                                     \
237                              *    UW = previous UW   (set above)            \
238                              *    5W = WT_FIVE_REPEAT                       \
239                              *    7W = previous CW  &  ISOLATE_WIDTH        \
240                              */                                             \
241                             EXTRA_WEIGHT_POS(1) = WT_FIVE_REPEAT;           \
242                             EXTRA_WEIGHT_POS(3) = PrevCW & ISOLATE_WIDTH;   \
243                         }                                                   \
244                         else                                                \
245                         {                                                   \
246                             /*                                              \
247                              *  Cho-On:                                     \
248                              *    UW = previous UW  &  CHO_ON_UW_MASK       \
249                              *    5W = WT_FIVE_CHO_ON                       \
250                              *    7W = current  CW  &  ISOLATE_WIDTH        \
251                              */                                             \
252                             *pPosUW &= CHO_ON_UW_MASK;                      \
253                             EXTRA_WEIGHT_POS(1) = WT_FIVE_CHO_ON;           \
254                             EXTRA_WEIGHT_POS(3) = XW & ISOLATE_WIDTH;       \
255                         }                                                   \
256                                                                             \
257                         pPosXW++;                                           \
258                     }                                                       \
259                     else                                                    \
260                     {                                                       \
261                         *pPosUW = GET_UNICODE(&PrevWt);                     \
262                     }                                                       \
263                                                                             \
264                     break;                                                  \
265                 }                                                           \
266                                                                             \
267                 /*                                                          \
268                  *  Make sure there is a valid UW.  If not, quit out        \
269                  *  of switch case.                                         \
270                  */                                                         \
271                 if (*pPosUW == MAP_INVALID_UW)                              \
272                 {                                                           \
273                     pPosUW++;                                               \
274                     break;                                                  \
275                 }                                                           \
276             }                                                               \
277             else                                                            \
278             {                                                               \
279                 /*                                                          \
280                  *  Kana:                                                   \
281                  *    SM = KANA                                             \
282                  *    AW = current AW                                       \
283                  *    4W = current CW  &  ISOLATE_SMALL                     \
284                  *    5W = WT_FIVE_KANA                                     \
285                  *    6W = current CW  &  ISOLATE_KANA                      \
286                  *    7W = current CW  &  ISOLATE_WIDTH                     \
287                  */                                                         \
288                 *pPosUW = MAKE_UNICODE_WT(KANA, AW);                        \
289                 EXTRA_WEIGHT_POS(0) = XW & ISOLATE_SMALL;                   \
290                 EXTRA_WEIGHT_POS(1) = WT_FIVE_KANA;                         \
291                 EXTRA_WEIGHT_POS(2) = XW & ISOLATE_KANA;                    \
292                 EXTRA_WEIGHT_POS(3) = XW & ISOLATE_WIDTH;                   \
293                                                                             \
294                 pPosXW++;                                                   \
295             }                                                               \
296                                                                             \
297             /*                                                              \
298              *  Always:                                                     \
299              *    DW = current DW                                           \
300              *    CW = minimum CW                                           \
301              */                                                             \
302             *pPosDW = GET_DIACRITIC(pWeight);                               \
303             *pPosCW = MIN_CW;                                               \
304                                                                             \
305             pPosUW++;                                                       \
306             pPosDW++;                                                       \
307             pPosCW++;                                                       \
308                                                                             \
309             break;                                                          \
310         }                                                                   \
311                                                                             \
312         case ( RESERVED_2 ) :                                               \
313         case ( RESERVED_3 ) :                                               \
314         {                                                                   \
315             /*                                                              \
316              *  Fill out the case statement so the compiler                 \
317              *  will use a jump table.                                      \
318              */                                                             \
319             ;                                                               \
320         }                                                                   \
321     }                                                                       \
322 }
323 
324 
325 BYTE pXWDrop[] =                  // values to drop from XW
326 {
327     0xc6,                         // weight 4
328     0x03,                         // weight 5
329     0xe4,                         // weight 6
330     0xc5                          // weight 7
331 };
332 BYTE pXWSeparator[] =             // separator values for XW
333 {
334     0xff,                         // weight 4
335     0x02,                         // weight 5
336     0xff,                         // weight 6
337     0xff                          // weight 7
338 };
339 
340 BYTE pAllWSeparator[] =
341 {
342   0x01, 0x01, 0xFF, 0x02, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00,
343   0x00, 0x00, 0x00, 0x00, 0x00, 0x00
344 };
345 
346 BOOL fBaseUsed = FALSE, fCBaseUsed = FALSE;
347 LOC_HASH *g_pLocale = NULL;
348 MAP_BUFF g_RootBuff={0};
349 
350 
351 MAP_BUFF *GetMapBuff(DWORD cbBuff)
352 {
353   MAP_BUFF *pBuf;
354 
355   //EnterCriticalSection(&g_CritSec);
356   for ( pBuf = &g_RootBuff; ; pBuf = pBuf->pNext )
357   {
358 	if ( cbBuff <= pBuf->cbBuff && !pBuf->bAllocated )
359 	{
360 		pBuf->bAllocated = TRUE;
361 		//LeaveCriticalSection(&g_CritSec);
362 		return pBuf;
363 	}
364 	if (!pBuf->pNext) break;
365   }
366   cbBuff += 4095;
367   cbBuff &= 0xFFFFF000;
368   pBuf->pNext = malloc(cbBuff + sizeof(MAP_BUFF));
369   pBuf = pBuf->pNext;
370   if (pBuf)
371   {
372     pBuf->pNext = NULL;
373     pBuf->cbBuff = cbBuff;
374     pBuf->bAllocated = TRUE;
375   }
376   //LeaveCriticalSection(&g_CritSec);
377   return pBuf;
378 }
379 
380 MAP_BUFF *FreeMapBuff(MAP_BUFF *pPBuf)
381 {
382   MAP_BUFF *pBuf;
383 
384   for (pBuf = &g_RootBuff; ; pBuf = pBuf->pNext)
385   {
386     if (pPBuf == pBuf)
387 	{
388       pBuf->bAllocated = FALSE;
389 	  break;
390 	}
391   }
392   return pBuf;
393 }
394 
395 PLOC_HASH DBCreateTables(int Locale, int uFlags, SORTKEY *pSortKeys)
396 {
397 	// TODO: Implement
398 	return NULL;
399 }
400 
401 PLOC_HASH SetSortInfo(LCID Locale, DWORD dwMapFlags)
402 {
403 	LCID Loc = Locale;
404 	PLOC_HASH pHashN;
405 
406 	if (dwMapFlags & LCMAP_LINGUISTIC_CASING) Loc*=-1;
407 	for (pHashN = (PLOC_HASH)&g_pLocale; pHashN; pHashN=pHashN->pNext)
408 	{
409 		if (pHashN->Locale == Loc) return pHashN;
410 		if (!pHashN->pNext) break;
411 	}
412 
413 #ifndef MDBTOOLS
414 	if (!fBaseUsed && Loc == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT))
415 	{
416 		if (!(pHashN->pNext=calloc(sizeof(LOC_HASH), 1))) return NULL;
417 		pHashN->pNext->Locale = Loc;
418 		pHashN->pNext->pSortkey = (PSORTKEY)g_SortKeys;
419 		pHashN->pNext->wUnk1 = 34;
420 		fBaseUsed = TRUE;
421 		return pHashN->pNext;
422 	}
423 #endif
424 
425 	if (!fCBaseUsed && (int)Loc == (int)(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT))*-1)
426 	{
427 		if (!(pHashN->pNext=calloc(sizeof(LOC_HASH), 1))) return NULL;
428 		pHashN->pNext->Locale = Loc;
429 		pHashN->pNext->pSortkey = (PSORTKEY)g_SortCompKeys;
430 		pHashN->pNext->wUnk1 = 127;
431 		fCBaseUsed = TRUE;
432 		return pHashN->pNext;
433 	}
434 	// MSWDAT10.DLL
435 	pHashN->pNext = DBCreateTables(Locale, dwMapFlags,
436 #ifndef MDBTOOLS
437 		fBaseUsed?NULL:(PSORTKEY)g_SortKeys
438 #else
439 		NULL
440 #endif
441 		);
442 	fBaseUsed = TRUE;
443 	return pHashN->pNext;
444 }
445 
446 
447 int WINAPI DBLCMapStringW(LCID Locale, int dwFlags, WCHAR *lpSrcString, signed int cchSrc, LPBYTE pDest, int cchDest)
448 {
449 	signed int cchExtra, cchSep;
450 	BYTE CaseMask;
451 	register int WeightLen;	// Length of one Weight set
452 	MAP_BUFF *pBuf;
453 	LPWSTR pUW;				// Unicode Weights
454 	LPBYTE pDW;                   // ptr to Diacritic Weights
455 	LPBYTE pCW;                   // ptr to Case Weights
456 	LPBYTE pXW;                   // ptr to Extra Weights
457 	LPWSTR pSW;                   // ptr to Special Weights
458 	LPWSTR pPosUW;				// pUW buf ptr
459 	LPBYTE pPosCW;				// pCW buf ptr
460 	LPBYTE pPosDW;				// pDW buf ptr
461 	LPBYTE pPosXW;				// ptr to position in pXW buffer
462 	LPWSTR pPosSW;                // ptr to position in pSW buffer
463 	PSORTKEY pWeight;			// weight of char ptr
464 	LPBYTE pTmp;                  // ptr to go through UW, XW, and SW
465 	LPBYTE pPosTmp, pPosOffset;               // ptr to tmp position in XW
466 	int PosCtr, ctr;
467 	LPWSTR pPos;
468 	PLOC_HASH pHashN;			// Hash table
469 	BYTE SM;					// Script member
470 	BOOL fStringSort;             // if using string sort method
471 	BOOL fIgnoreSymbols;          // if ignore symbols flag is set
472 
473 	if (!lpSrcString || !(pHashN = SetSortInfo(Locale, dwFlags))) return 0;
474 	if (cchSrc <= -1) cchSrc = NlsStrLenW(lpSrcString) + 1;
475 
476 	if (dwFlags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE))
477 	{
478 		PCASE pMapTable;
479 
480 		if (dwFlags & LCMAP_LOWERCASE)
481 		{
482 			if (dwFlags & LCMAP_LINGUISTIC_CASING)
483 			{
484 				pMapTable = g_LinguisticLowerCase;
485 				for (PosCtr = 0; g_SpecialCase[PosCtr].Locale; PosCtr++)
486 				{
487 					if (g_SpecialCase[PosCtr].Locale == Locale)
488 					{
489 						pMapTable = g_SpecialCase[PosCtr].pLowerCase;
490 						break;
491 					}
492 				}
493 			}
494 			else
495 			{
496 				pMapTable = g_LowerCase;
497 			}
498 		}
499 		else
500 		{
501 			if (dwFlags & LCMAP_LINGUISTIC_CASING)
502 			{
503 				pMapTable = g_LinguisticUpperCase;
504 				for (PosCtr = 0; g_SpecialCase[PosCtr].Locale; PosCtr++)
505 				{
506 					if (g_SpecialCase[PosCtr].Locale == Locale)
507 					{
508 						pMapTable = g_SpecialCase[PosCtr].pUpperCase;
509 						break;
510 					}
511 				}
512 			}
513 			else
514 			{
515 				pMapTable = g_UpperCase;
516 			}
517 		}
518 		if ( cchSrc > 0 )
519 		{
520 			for (PosCtr = 0; PosCtr < cchSrc; PosCtr++)
521 			{
522 				pDest[PosCtr] = lpSrcString[PosCtr] + g_CasePages[pMapTable[lpSrcString[PosCtr]]][lpSrcString[PosCtr]];
523 			}
524 		}
525 		return PosCtr;
526 	}
527 
528 	// TODO: Invalid Flags Check
529 	WeightLen = cchSrc * MAX_EXPANSION;
530 	if (!(dwFlags & LCMAP_SORTKEY) || !(pBuf = GetMapBuff(WeightLen * MAX_WEIGHTS * sizeof(WCHAR))))
531 		return 0;
532 
533 	pUW = (LPWSTR)((DWORD)pBuf + sizeof(MAP_BUFF));
534 	pDW = (LPBYTE)(pUW + (WeightLen * (NUM_BYTES_UW / sizeof(WCHAR))));
535 	pCW = (LPBYTE)(pDW + (WeightLen * NUM_BYTES_DW));
536 	pXW = (LPBYTE)(pCW + (WeightLen * NUM_BYTES_CW));
537 	pSW = (LPWSTR)(pXW + (WeightLen * NUM_BYTES_XW));
538 	pPosUW = pUW;
539 	pPosDW = pDW;
540 	pPosCW = pCW;
541 	pPosXW = pXW;
542 	pPosSW = pSW;
543 
544 	CaseMask = 0xFF;
545 	if ( dwFlags & NORM_IGNORECASE )
546 		CaseMask = CASE_UPPER_MASK;
547 	if ( dwFlags & NORM_IGNOREKANATYPE )
548 		CaseMask &= CASE_KANA_MASK;
549 	if ( dwFlags & NORM_IGNOREWIDTH )
550 		CaseMask &= CASE_WIDTH_MASK;
551 
552 	fStringSort = dwFlags & SORT_STRINGSORT;
553 	fIgnoreSymbols = dwFlags & NORM_IGNORESYMBOLS;
554 	pPos = lpSrcString;
555 	if ( cchSrc >= 1 )
556 	{
557 		// TODO: JUMPOUT(pHashN[4], 0, &loc_10001DFF);	// IfCompression?
558 		for (PosCtr = 1; PosCtr <= cchSrc; PosCtr++, pPos++)
559 		{
560 			pWeight = (PSORTKEY)(&(pHashN->pSortkey)[*pPos]);
561 			SM = GET_SCRIPT_MEMBER(pWeight);
562 
563 			if (SM > MAX_SPECIAL_CASE)
564 			{
565 				*pPosUW = UNICODE_WT(pWeight);
566 				*pPosDW = GET_DIACRITIC(pWeight);
567 				*pPosCW = GET_CASE(pWeight) & CaseMask;
568 				pPosUW++;
569 				pPosDW++;
570 				pPosCW++;
571 			}
572 			else
573 			{
574                 SPECIAL_CASE_HANDLER( SM,
575                                       pWeight,
576                                       pHashN->pSortkey,
577                                       g_Expansion,
578                                       pPosUW - pUW + 1,
579                                       fStringSort,
580                                       fIgnoreSymbols,
581                                       pPos,
582                                       (LPWSTR)lpSrcString );
583 			}
584 		}
585 	}
586 	PosCtr = 0;
587 	if (cchDest == 0)
588 	{
589 		if ( dwFlags & LCMAP_LINGUISTIC_CASING )
590 		{
591 			LPBYTE pUWTmp;
592 
593 			for (pUWTmp = (LPBYTE)pUW; pUWTmp != (LPBYTE)pPosUW; pUWTmp++)
594 				if (*pUWTmp) ++PosCtr;
595 		}
596 		else
597 		{
598 			PosCtr += ((LPBYTE)pPosUW - (LPBYTE)pUW);
599 		}
600 		PosCtr++;
601 		if (!(dwFlags & NORM_IGNORENONSPACE))
602 		{
603 			pPosDW--;
604 			if (pHashN->IfReverseDW == TRUE)
605 			{
606 				while ((pDW <= pPosDW) && (*pDW <= MIN_DW))
607 				{
608 					pDW++;
609 				}
610 				PosCtr += (pPosDW - pDW + 1);
611 			}
612 			else
613 			{
614 				while ((pPosDW >= pDW) && (*pPosDW <= MIN_DW))
615 				{
616 					pPosDW--;
617 				}
618 				PosCtr += (pPosDW - pDW + 1);
619 			}
620 		}
621 
622 		PosCtr++;
623 
624 		if ((dwFlags & NORM_DROP_CW) != NORM_DROP_CW)
625 		{
626 			pPosCW--;
627 			while ((pPosCW >= pCW) && (*pPosCW <= MIN_CW))
628 			{
629 				pPosCW--;
630 			}
631 			PosCtr += (pPosCW - pCW + 1);
632 		}
633 
634 		PosCtr++;
635 
636 
637 		if (pXW < pPosXW)
638 		{
639 			if (dwFlags & NORM_IGNORENONSPACE)
640 			{
641 				PosCtr += 2;
642 				ctr = 2;
643 			}
644 			else
645 			{
646 				ctr = 0;
647 			}
648 			pPosXW--;
649 			for (; ctr < NUM_BYTES_XW; ctr++)
650 			{
651 				pTmp = pXW + (WeightLen * ctr);
652 				pPosTmp = pPosXW + (WeightLen * ctr);
653 				while ((pPosTmp >= pTmp) && (*pPosTmp == pXWDrop[ctr]))
654 				{
655 					pPosTmp--;
656 				}
657 				if ( dwFlags & LCMAP_LINGUISTIC_CASING )
658 				{
659 					PosCtr += (pPosTmp - pTmp + 1 + gXW_comp[ctr][3]) / (gXW_comp[ctr][3]+1);
660 				}
661 				else
662 				{
663 					PosCtr += (pPosTmp - pTmp + 1);
664 				}
665 				PosCtr++;
666 			}
667 		}
668 
669 		PosCtr++;
670 
671 		if (!fIgnoreSymbols)
672 		{
673 			PosCtr += ((LPBYTE)pPosSW - (LPBYTE)pSW);
674 		}
675 
676 		PosCtr++;
677 	}
678 	else
679 	{
680 		if (dwFlags & LOCALE_NOUSEROVERRIDE)
681 		{
682 		// TODO: JUMPOUT(dwFlags & LOCALE_NOUSEROVERRIDE, 0, &loc_10002502);
683 
684 		}
685 
686 		cchExtra = 0;
687 		cchSep = 0;
688 		if ( pPosUW != pUW )
689 		{
690 			if (dwFlags & LCMAP_LINGUISTIC_CASING)
691 			{
692 				LPBYTE pUWTmp;
693 
694 				for (pUWTmp = (LPBYTE)pUW; pUWTmp != (LPBYTE)pPosUW; pUWTmp++)
695 					if (*pUWTmp) pDest[PosCtr++]=*pUWTmp;
696 			}
697 			else
698 			{
699 				PosCtr = ((LPBYTE)pPosUW - (LPBYTE)pUW);
700 				if (cchDest < PosCtr)
701 				{
702 					FreeMapBuff(pBuf);
703 					return 0;
704 				}
705 				memcpy (pDest, pUW, PosCtr);
706 
707 			}
708 		}
709 		if (cchDest < (PosCtr + 1))
710 		{
711 			FreeMapBuff(pBuf);
712 			return 0;
713 		}
714 		pDest[PosCtr] = SORTKEY_SEPARATOR;
715 		PosCtr++;
716 
717 
718 		if (!(dwFlags & NORM_IGNORENONSPACE))
719 		{
720 			pPosDW--;
721 			if (pHashN->IfReverseDW == TRUE)
722 			{
723 				while ((pDW <= pPosDW) && (*pDW <= MIN_DW))
724 				{
725 					pDW++;
726 				}
727 				if ((cchDest - PosCtr) <= (pPosDW - pDW + 1))
728 				{
729 					FreeMapBuff(pBuf);
730 					return (0);
731 				}
732 				while (pPosDW >= pDW)
733 				{
734 					pDest[PosCtr] = *pPosDW;
735 					PosCtr++;
736 					pPosDW--;
737 				}
738 			}
739 			else
740 			{
741 				while ((pPosDW >= pDW) && (*pPosDW <= MIN_DW))
742 				{
743 					pPosDW--;
744 				}
745 				if ((cchDest - PosCtr) <= (pPosDW - pDW + 1))
746 				{
747 					FreeMapBuff(pBuf);
748 					return (0);
749 				}
750 				memcpy(&pDest[PosCtr], pDW, pPosDW - pDW + 1);
751 				PosCtr += pPosDW - pDW + 1;
752 			}
753 		}
754 
755 
756 		if ( PosCtr >= cchDest )
757 		{
758 			if (dwFlags & LCMAP_LINGUISTIC_CASING)
759 			{
760 				FreeMapBuff(pBuf);
761 				return (0);
762 			}
763 		}
764 		else
765 		{
766 			pDest[PosCtr] = SORTKEY_SEPARATOR;
767 			PosCtr++;
768 
769 			if ( PosCtr < cchDest )
770 				cchExtra = 1;
771 		}
772 
773 		if ((dwFlags & NORM_DROP_CW) != NORM_DROP_CW)
774 		{
775 			pPosCW--;
776 			while ((pPosCW >= pCW) && (*pPosCW <= MIN_CW))
777 			{
778 				pPosCW--;
779 			}
780 			if ((cchDest - PosCtr) <= (pPosCW - pCW + 1))
781 			{
782 				FreeMapBuff(pBuf);
783 				return (0);
784 			}
785 			memcpy(&pDest[PosCtr], pCW, pPosCW - pCW);
786 			PosCtr += pPosCW - pCW;
787 		}
788 		if ( PosCtr >= cchDest )
789 		{
790 			if (dwFlags & LCMAP_LINGUISTIC_CASING)
791 			{
792 				FreeMapBuff(pBuf);
793 				return (0);
794 			}
795 		}
796 		else
797 		{
798 			pDest[PosCtr] = SORTKEY_SEPARATOR;
799 			PosCtr++;
800 			if ( PosCtr < cchDest ) ++cchExtra;
801 		}
802 
803 	//JUMPOUT(pXW, pPosXW, sub_1000262D);
804 		if (pXW < pPosXW)
805 		{
806 			if (dwFlags & NORM_IGNORENONSPACE)
807 			{
808 				if ( PosCtr >= cchDest )
809 				{
810 					if (dwFlags & LCMAP_LINGUISTIC_CASING)
811 					{
812 						FreeMapBuff(pBuf);
813 						return (0);
814 					}
815 				}
816 				else
817 				{
818 					pDest[PosCtr++] = pXWSeparator[0];
819 					if ( PosCtr >= cchDest )
820 					{
821 						if (dwFlags & LCMAP_LINGUISTIC_CASING)
822 						{
823 							FreeMapBuff(pBuf);
824 							return (0);
825 						}
826 					}
827 					else
828 					{
829 						cchSep = 1;
830 						++cchExtra;
831 						pDest[PosCtr++] = pXWSeparator[1];
832 						if ( PosCtr < cchDest ) ++cchExtra;
833 					}
834 					ctr = 2;
835 				}
836 			}
837 			else
838 			{
839 				ctr = 0;
840 			}
841 
842 			for (; ctr < NUM_BYTES_XW; ctr++)
843 			{
844 				if ( pPosXW - pXW )
845 				{
846 					pTmp = pXW + (WeightLen * ctr);
847 					pPosTmp = pPosXW + (WeightLen * ctr);
848 					pPosOffset = (LPBYTE)(pPosXW - pXW);
849 
850 					while ((pPosTmp >= pTmp) && (*pPosTmp == pXWDrop[ctr]))
851 					{
852 						pPosTmp--;
853 					}
854 					if ( (dwFlags & LCMAP_LINGUISTIC_CASING) )
855 					{
856 						BYTE pNumOffs, c;
857 
858 						for (c = 0x80, pNumOffs=(BYTE)gXW_comp[ctr][3]; pPosTmp > pTmp; pTmp++)
859 						{
860 							c |= (BYTE)((ctr==1?((*pTmp>>1)|*pTmp&1):*pTmp) & gXW_comp[ctr][0]) >>
861 							gXW_comp[ctr][1] << pNumOffs * gXW_comp[ctr][2];
862 							if (pNumOffs)
863 							{
864 								pNumOffs--;
865 								continue;
866 							}
867 							if ( cchDest - PosCtr < 2)
868 							{
869 								FreeMapBuff(pBuf);
870 								return (0);
871 							}
872 							pDest[PosCtr++] = c;
873 							pNumOffs = gXW_comp[ctr][3];
874 							c = 0x80;
875 						}
876 						if (pNumOffs != gXW_comp[ctr][3] && cchDest - PosCtr >= 2) pDest[PosCtr++] = c;
877 					}
878 					else
879 					{
880 						if ((cchDest - PosCtr) <= (pPosTmp - pTmp + 1))
881 						{
882 							FreeMapBuff(pBuf);
883 							return (0);
884 						}
885 						memcpy(&pDest[PosCtr], pTmp, pPosTmp - pTmp);
886 						PosCtr += pPosTmp - pTmp;
887 					}
888 				}
889 				if ( PosCtr >= cchDest )
890 				{
891 					if (dwFlags & LCMAP_LINGUISTIC_CASING)
892 					{
893 						FreeMapBuff(pBuf);
894 						return (0);
895 					}
896 				}
897 				else
898 				{
899 					pDest[PosCtr] = pXWSeparator[ctr];
900 					PosCtr++;
901 					if ( PosCtr < cchDest )
902 					{
903 						++cchExtra;
904 						++cchSep;
905 					}
906 				}
907 			}
908 
909 			if ( PosCtr >= cchDest )
910 			{
911 				if (dwFlags & LCMAP_LINGUISTIC_CASING)
912 				{
913 					FreeMapBuff(pBuf);
914 					return (0);
915 				}
916 			}
917 		}
918 
919 		pDest[PosCtr] = SORTKEY_SEPARATOR;
920 		PosCtr++;
921 
922 		if ( PosCtr < cchDest ) cchExtra++;
923 		if (!fIgnoreSymbols)
924 		{
925 			if ((cchDest - PosCtr) <= ((LPBYTE)pPosSW - (LPBYTE)pSW))
926 			{
927 				FreeMapBuff(pBuf);
928 				return (0);
929 			}
930 			memcpy(&pDest[PosCtr], pSW, (LPBYTE)pPosSW - (LPBYTE)pSW);
931 			PosCtr += (LPBYTE)pPosSW - (LPBYTE)pSW;
932 		}
933 
934 		if ( PosCtr >= cchDest )
935 		{
936 			if (dwFlags & LCMAP_LINGUISTIC_CASING)
937 			{
938 				FreeMapBuff(pBuf);
939 				return (0);
940 			}
941 		}
942 
943 		pDest[PosCtr] = SORTKEY_TERMINATOR;
944 		if ( cchExtra && (dwFlags & LCMAP_LINGUISTIC_CASING) )
945 		{
946 			do
947 			{
948 				--cchExtra;
949 				if ( (cchSep && pDest[PosCtr - 2] != pAllWSeparator[cchExtra]) || pDest[PosCtr - 2] != SORTKEY_SEPARATOR )
950 					break;
951 				pDest[PosCtr - 1] = SORTKEY_TERMINATOR;
952 				PosCtr--;
953 			}
954 			while ( cchExtra );
955 		}
956 
957 	}
958 	FreeMapBuff(pBuf);
959 	return PosCtr;
960 }
961