changed: don't include event.h in stdafx.h
[xbmc:xbmc-antiquated.git] / xbmc / utils / CharsetConverter.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "stdafx.h"
23 #include "Util.h"
24 #include "ArabicShaping.h"
25 #include "GUISettings.h"
26 #include "LangInfo.h"
27 #include "SingleLock.h"
28
29 #ifndef _LINUX
30 #include "lib/libiconv/iconv.h"
31 #else
32 #include <iconv.h>
33 #endif
34
35 #ifdef __APPLE__
36 #ifdef __POWERPC__
37   #define WCHAR_CHARSET "UTF-32BE"
38 #else
39   #define WCHAR_CHARSET "UTF-32LE"
40 #endif
41   #define UTF8_SOURCE "UTF-8-MAC"
42 #elif defined(WIN32)
43   #define WCHAR_CHARSET "UTF-16LE"
44   #define UTF8_SOURCE "UTF-8"
45 #else
46   #define WCHAR_CHARSET "WCHAR_T"
47   #define UTF8_SOURCE "UTF-8"
48 #endif
49
50
51 static iconv_t m_iconvStringCharsetToFontCharset = (iconv_t)-1;
52 static iconv_t m_iconvSubtitleCharsetToW         = (iconv_t)-1;
53 static iconv_t m_iconvUtf8ToStringCharset        = (iconv_t)-1;
54 static iconv_t m_iconvStringCharsetToUtf8        = (iconv_t)-1;
55 static iconv_t m_iconvUcs2CharsetToStringCharset = (iconv_t)-1;
56 static iconv_t m_iconvUtf32ToStringCharset       = (iconv_t)-1;
57 static iconv_t m_iconvWtoUtf8                    = (iconv_t)-1;
58 static iconv_t m_iconvUtf16LEtoW                 = (iconv_t)-1;
59 static iconv_t m_iconvUtf16BEtoUtf8              = (iconv_t)-1;
60 static iconv_t m_iconvUtf16LEtoUtf8              = (iconv_t)-1;
61 static iconv_t m_iconvUtf8toW                    = (iconv_t)-1;
62 static iconv_t m_iconvUcs2CharsetToUtf8          = (iconv_t)-1;
63
64 static FriBidiCharSet m_stringFribidiCharset     = FRIBIDI_CHAR_SET_NOT_FOUND;
65
66 static std::vector<CStdString>     m_vecCharsetNames;
67 static std::vector<CStdString>     m_vecCharsetLabels;
68 static std::vector<CStdString>     m_vecBidiCharsetNames;
69 static std::vector<FriBidiCharSet> m_vecBidiCharsets;
70 static CCriticalSection            m_critSection;
71
72 CCharsetConverter g_charsetConverter;
73
74 #define UTF8_DEST_MULTIPLIER 6
75
76 #define ICONV_PREPARE(iconv) iconv=(iconv_t)-1
77 #define ICONV_SAFE_CLOSE(iconv) if (iconv!=(iconv_t)-1) { iconv_close(iconv); iconv=(iconv_t)-1; }
78
79 size_t iconv_const (void* cd, const char** inbuf, size_t *inbytesleft,
80                     char* * outbuf, size_t *outbytesleft)
81 {
82     struct iconv_param_adapter {
83         iconv_param_adapter(const char**p) : p(p) {}
84         iconv_param_adapter(char**p) : p((const char**)p) {}
85         operator char**() const
86         {
87             return(char**)p;
88         }
89         operator const char**() const
90         {
91             return(const char**)p;
92         }
93         const char** p;
94     };
95
96     return iconv((iconv_t)cd, iconv_param_adapter(inbuf), inbytesleft, outbuf, outbytesleft);
97 }
98
99 template<class INPUT,class OUTPUT>
100 static bool convert_checked(iconv_t& type, int multiplier, const CStdString& strFromCharset, const CStdString& strToCharset, const INPUT& strSource,  OUTPUT& strDest)
101 {
102   if (type == (iconv_t) - 1)
103   {
104     type = iconv_open(strToCharset.c_str(), strFromCharset.c_str());
105   }
106
107   if (type != (iconv_t) - 1 && strSource.length())
108   {
109     size_t inBytes  = (strSource.length() + 1)*sizeof(strSource[0]);
110     size_t outBytes = (strSource.length() + 1)*multiplier;
111     const char *src = (const char*)strSource.c_str();
112     char       *dst = (char*)strDest.GetBuffer(outBytes);
113
114     if (iconv_const(type, &src, &inBytes, &dst, &outBytes) == (size_t)-1)
115     {
116       CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
117       strDest.ReleaseBuffer();
118       return false;
119     }
120
121     if (iconv_const(type, NULL, NULL, &dst, &outBytes) == (size_t)-1)
122     {
123       CLog::Log(LOGERROR, "%s failed cleanup", __FUNCTION__);
124       strDest.ReleaseBuffer();
125       return false;
126     }
127
128     strDest.ReleaseBuffer();
129   }
130   return true;
131 }
132
133 template<class INPUT,class OUTPUT>
134 static void convert(iconv_t& type, int multiplier, const CStdString& strFromCharset, const CStdString& strToCharset, const INPUT& strSource,  OUTPUT& strDest)
135 {
136   if(!convert_checked(type, multiplier, strFromCharset, strToCharset, strSource, strDest))
137     strDest = strSource;
138 }
139
140 using namespace std;
141
142 static void logicalToVisualBiDi(const CStdStringA& strSource, CStdStringA& strDest, FriBidiCharSet fribidiCharset, FriBidiCharType base = FRIBIDI_TYPE_LTR, bool* bWasFlipped =NULL)
143 {
144   // libfribidi is not threadsafe, so make sure we make it so
145   CSingleLock lock(m_critSection);
146
147   vector<CStdString> lines;
148   CUtil::Tokenize(strSource, lines, "\n");
149   CStdString resultString;
150
151   if (bWasFlipped)
152     *bWasFlipped = false;
153
154   for (unsigned int i = 0; i < lines.size(); i++)
155   {
156     int sourceLen = lines[i].length();
157
158     // Convert from the selected charset to Unicode
159     FriBidiChar* logical = (FriBidiChar*) malloc((sourceLen + 1) * sizeof(FriBidiChar));
160     int len = fribidi_charset_to_unicode(fribidiCharset, (char*) lines[i].c_str(), sourceLen, logical);
161
162     FriBidiChar* visual = (FriBidiChar*) malloc((len + 1) * sizeof(FriBidiChar));
163     FriBidiLevel* levels = (FriBidiLevel*) malloc((len + 1) * sizeof(FriBidiLevel));
164
165     // Shape Arabic Text
166     FriBidiChar *shaped_text = shape_arabic(logical, len);
167     for (int i = 0; i < len; i++)
168        logical[i] = shaped_text[i];
169     free(shaped_text);
170
171     if (fribidi_log2vis(logical, len, &base, visual, NULL, NULL, NULL))
172     {
173       // Removes bidirectional marks
174       //len = fribidi_remove_bidi_marks(visual, len, NULL, NULL, NULL);
175
176       // Apperently a string can get longer during this transformation
177       // so make sure we allocate the maximum possible character utf8
178       // can generate atleast, should cover all bases
179       char *result = strDest.GetBuffer(len*4);
180
181       // Convert back from Unicode to the charset
182       int len2 = fribidi_unicode_to_charset(fribidiCharset, visual, len, result);
183       ASSERT(len2 <= len*4);
184       strDest.ReleaseBuffer();
185
186       resultString += strDest;
187
188       // Check whether the string was flipped if one of the embedding levels is greater than 0
189       if (bWasFlipped && !*bWasFlipped)
190       {
191         for (int i = 0; i < len; i++)
192         {
193           if ((int) levels[i] > 0)
194           {
195             *bWasFlipped = true;
196             break;
197           }
198         }
199       }
200     }
201
202     free(logical);
203     free(visual);
204     free(levels);
205   }
206
207   strDest = resultString;
208 }
209
210 CCharsetConverter::CCharsetConverter()
211 {
212   m_vecCharsetNames.push_back("ISO-8859-1");
213   m_vecCharsetLabels.push_back("Western Europe (ISO)");
214   m_vecCharsetNames.push_back("ISO-8859-2");
215   m_vecCharsetLabels.push_back("Central Europe (ISO)");
216   m_vecCharsetNames.push_back("ISO-8859-3");
217   m_vecCharsetLabels.push_back("South Europe (ISO)");
218   m_vecCharsetNames.push_back("ISO-8859-4");
219   m_vecCharsetLabels.push_back("Baltic (ISO)");
220   m_vecCharsetNames.push_back("ISO-8859-5");
221   m_vecCharsetLabels.push_back("Cyrillic (ISO)");
222   m_vecCharsetNames.push_back("ISO-8859-6");
223   m_vecCharsetLabels.push_back("Arabic (ISO)");
224   m_vecCharsetNames.push_back("ISO-8859-7");
225   m_vecCharsetLabels.push_back("Greek (ISO)");
226   m_vecCharsetNames.push_back("ISO-8859-8");
227   m_vecCharsetLabels.push_back("Hebrew (ISO)");
228   m_vecCharsetNames.push_back("ISO-8859-9");
229   m_vecCharsetLabels.push_back("Turkish (ISO)");
230
231   m_vecCharsetNames.push_back("CP1250");
232   m_vecCharsetLabels.push_back("Central Europe (Windows)");
233   m_vecCharsetNames.push_back("CP1251");
234   m_vecCharsetLabels.push_back("Cyrillic (Windows)");
235   m_vecCharsetNames.push_back("CP1252");
236   m_vecCharsetLabels.push_back("Western Europe (Windows)");
237   m_vecCharsetNames.push_back("CP1253");
238   m_vecCharsetLabels.push_back("Greek (Windows)");
239   m_vecCharsetNames.push_back("CP1254");
240   m_vecCharsetLabels.push_back("Turkish (Windows)");
241   m_vecCharsetNames.push_back("CP1255");
242   m_vecCharsetLabels.push_back("Hebrew (Windows)");
243   m_vecCharsetNames.push_back("CP1256");
244   m_vecCharsetLabels.push_back("Arabic (Windows)");
245   m_vecCharsetNames.push_back("CP1257");
246   m_vecCharsetLabels.push_back("Baltic (Windows)");
247   m_vecCharsetNames.push_back("CP1258");
248   m_vecCharsetLabels.push_back("Vietnamesse (Windows)");
249   m_vecCharsetNames.push_back("CP874");
250   m_vecCharsetLabels.push_back("Thai (Windows)");
251
252   m_vecCharsetNames.push_back("BIG5");
253   m_vecCharsetLabels.push_back("Chinese Traditional (Big5)");
254   m_vecCharsetNames.push_back("GBK");
255   m_vecCharsetLabels.push_back("Chinese Simplified (GBK)");
256   m_vecCharsetNames.push_back("SHIFT_JIS");
257   m_vecCharsetLabels.push_back("Japanese (Shift-JIS)");
258   m_vecCharsetNames.push_back("CP949");
259   m_vecCharsetLabels.push_back("Korean");
260   m_vecCharsetNames.push_back("BIG5-HKSCS");
261   m_vecCharsetLabels.push_back("Hong Kong (Big5-HKSCS)");
262
263   m_vecBidiCharsetNames.push_back("ISO-8859-6");
264   m_vecBidiCharsetNames.push_back("ISO-8859-8");
265   m_vecBidiCharsetNames.push_back("CP1255");
266   m_vecBidiCharsetNames.push_back("Windows-1255");
267   m_vecBidiCharsetNames.push_back("CP1256");
268   m_vecBidiCharsetNames.push_back("Windows-1256");
269   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_ISO8859_6);
270   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_ISO8859_8);
271   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_CP1255);
272   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_CP1255);
273   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_CP1256);
274   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_CP1256);
275 }
276
277 void CCharsetConverter::clear()
278 {
279   CSingleLock lock(m_critSection);
280
281   m_vecBidiCharsetNames.clear();
282   m_vecBidiCharsets.clear();
283   m_vecCharsetNames.clear();
284   m_vecCharsetLabels.clear();
285 }
286
287 vector<CStdString> CCharsetConverter::getCharsetLabels()
288 {
289   return m_vecCharsetLabels;
290 }
291
292 CStdString& CCharsetConverter::getCharsetLabelByName(const CStdString& charsetName)
293 {
294   for (unsigned int i = 0; i < m_vecCharsetNames.size(); i++)
295   {
296     if (m_vecCharsetNames[i].Equals(charsetName))
297     {
298       return m_vecCharsetLabels[i];
299     }
300   }
301
302   return EMPTY;
303 }
304
305 CStdString& CCharsetConverter::getCharsetNameByLabel(const CStdString& charsetLabel)
306 {
307   CSingleLock lock(m_critSection);
308
309   for (unsigned int i = 0; i < m_vecCharsetLabels.size(); i++)
310   {
311     if (m_vecCharsetLabels[i].Equals(charsetLabel))
312     {
313       return m_vecCharsetNames[i];
314     }
315   }
316
317   return EMPTY;
318 }
319
320 bool CCharsetConverter::isBidiCharset(const CStdString& charset)
321 {
322   CSingleLock lock(m_critSection);
323
324   for (unsigned int i = 0; i < m_vecBidiCharsetNames.size(); i++)
325   {
326     if (m_vecBidiCharsetNames[i].Equals(charset))
327     {
328       return true;
329     }
330   }
331
332   return false;
333 }
334
335 void CCharsetConverter::reset(void)
336 {
337   CSingleLock lock(m_critSection);
338
339   ICONV_SAFE_CLOSE(m_iconvStringCharsetToFontCharset);
340   ICONV_SAFE_CLOSE(m_iconvUtf8ToStringCharset);
341   ICONV_SAFE_CLOSE(m_iconvStringCharsetToUtf8);
342   ICONV_SAFE_CLOSE(m_iconvUcs2CharsetToStringCharset);
343   ICONV_SAFE_CLOSE(m_iconvSubtitleCharsetToW);
344   ICONV_SAFE_CLOSE(m_iconvWtoUtf8);
345   ICONV_SAFE_CLOSE(m_iconvUtf16BEtoUtf8);
346   ICONV_SAFE_CLOSE(m_iconvUtf16LEtoUtf8);
347   ICONV_SAFE_CLOSE(m_iconvUtf32ToStringCharset);
348   ICONV_SAFE_CLOSE(m_iconvUtf8toW);
349   ICONV_SAFE_CLOSE(m_iconvUcs2CharsetToUtf8);
350
351   m_stringFribidiCharset = FRIBIDI_CHAR_SET_NOT_FOUND;
352
353   CStdString strCharset=g_langInfo.GetGuiCharSet();
354
355   for (unsigned int i = 0; i < m_vecBidiCharsetNames.size(); i++)
356   {
357     if (m_vecBidiCharsetNames[i].Equals(strCharset))
358     {
359       m_stringFribidiCharset = m_vecBidiCharsets[i];
360     }
361   }
362 }
363
364 // The bVisualBiDiFlip forces a flip of characters for hebrew/arabic languages, only set to false if the flipping
365 // of the string is already made or the string is not displayed in the GUI
366 void CCharsetConverter::utf8ToW(const CStdStringA& utf8String, CStdStringW &wString, bool bVisualBiDiFlip/*=true*/, bool forceLTRReadingOrder /*=false*/, bool* bWasFlipped/*=NULL*/)
367 {
368   CStdStringA strFlipped;
369
370   // Try to flip hebrew/arabic characters, if any
371   if (bVisualBiDiFlip)
372   {
373     FriBidiCharType charset = forceLTRReadingOrder ? FRIBIDI_TYPE_LTR : FRIBIDI_TYPE_PDF;
374     logicalToVisualBiDi(utf8String, strFlipped, FRIBIDI_CHAR_SET_UTF8, charset, bWasFlipped);
375     convert(m_iconvUtf8toW,sizeof(wchar_t),UTF8_SOURCE,WCHAR_CHARSET,strFlipped,wString);
376   }
377   else
378     convert(m_iconvUtf8toW,sizeof(wchar_t),UTF8_SOURCE,WCHAR_CHARSET,utf8String,wString);
379 }
380
381 void CCharsetConverter::subtitleCharsetToW(const CStdStringA& strSource, CStdStringW& strDest)
382 {
383   // No need to flip hebrew/arabic as mplayer does the flipping
384   CSingleLock lock(m_critSection);
385   convert(m_iconvSubtitleCharsetToW,sizeof(wchar_t),g_langInfo.GetSubtitleCharSet(),WCHAR_CHARSET,strSource,strDest);
386 }
387
388 void CCharsetConverter::utf8ToStringCharset(const CStdStringA& strSource, CStdStringA& strDest)
389 {
390   CSingleLock lock(m_critSection);
391   convert(m_iconvUtf8ToStringCharset,1,UTF8_SOURCE,g_langInfo.GetGuiCharSet(),strSource,strDest);
392 }
393
394 void CCharsetConverter::utf8ToStringCharset(CStdStringA& strSourceDest)
395 {
396   CStdString strDest;
397   utf8ToStringCharset(strSourceDest, strDest);
398   strSourceDest=strDest;
399 }
400
401 void CCharsetConverter::stringCharsetToUtf8(const CStdStringA& strSourceCharset, const CStdStringA& strSource, CStdStringA& strDest)
402 {
403   iconv_t iconvString;
404   ICONV_PREPARE(iconvString);
405   convert(iconvString,UTF8_DEST_MULTIPLIER,strSourceCharset,"UTF-8",strSource,strDest);
406   iconv_close(iconvString);
407 }
408
409 void CCharsetConverter::utf8To(const CStdStringA& strDestCharset, const CStdStringA& strSource, CStdStringA& strDest)
410 {
411   if (strDestCharset == "UTF-8")
412   { // simple case - no conversion necessary
413     strDest = strSource;
414     return;
415   }
416   iconv_t iconvString;
417   ICONV_PREPARE(iconvString);
418   convert(iconvString,UTF8_DEST_MULTIPLIER,UTF8_SOURCE,strDestCharset,strSource,strDest);
419   iconv_close(iconvString);
420 }
421
422 void CCharsetConverter::utf8To(const CStdStringA& strDestCharset, const CStdStringA& strSource, CStdString16& strDest)
423 {
424   iconv_t iconvString;
425   ICONV_PREPARE(iconvString);
426   if(!convert_checked(iconvString,UTF8_DEST_MULTIPLIER,UTF8_SOURCE,strDestCharset,strSource,strDest))
427     strDest.Empty();
428   iconv_close(iconvString);
429 }
430
431 void CCharsetConverter::utf8To(const CStdStringA& strDestCharset, const CStdStringA& strSource, CStdString32& strDest)
432 {
433   iconv_t iconvString;
434   ICONV_PREPARE(iconvString);
435   if(!convert_checked(iconvString,UTF8_DEST_MULTIPLIER,UTF8_SOURCE,strDestCharset,strSource,strDest))
436     strDest.Empty();
437   iconv_close(iconvString);
438 }
439
440 void CCharsetConverter::unknownToUTF8(CStdStringA &sourceAndDest)
441 {
442   CStdString source = sourceAndDest;
443   unknownToUTF8(source, sourceAndDest);
444 }
445
446 void CCharsetConverter::unknownToUTF8(const CStdStringA &source, CStdStringA &dest)
447 {
448   // checks whether it's utf8 already, and if not converts using the sourceCharset if given, else the string charset
449   if (isValidUtf8(source))
450     dest = source;
451   else
452   {
453     CSingleLock lock(m_critSection);
454     convert(m_iconvStringCharsetToUtf8, UTF8_DEST_MULTIPLIER, g_langInfo.GetGuiCharSet(), "UTF-8", source, dest);
455   }
456 }
457
458 void CCharsetConverter::wToUTF8(const CStdStringW& strSource, CStdStringA &strDest)
459 {
460   CSingleLock lock(m_critSection);
461   convert(m_iconvWtoUtf8,UTF8_DEST_MULTIPLIER,WCHAR_CHARSET,"UTF-8",strSource,strDest);
462 }
463
464 void CCharsetConverter::utf16BEtoUTF8(const CStdString16& strSource, CStdStringA &strDest)
465 {
466   CSingleLock lock(m_critSection);
467   if(!convert_checked(m_iconvUtf16BEtoUtf8,UTF8_DEST_MULTIPLIER,"UTF-16BE","UTF-8",strSource,strDest))
468     strDest.empty();
469 }
470
471 void CCharsetConverter::utf16LEtoUTF8(const CStdString16& strSource,
472                                       CStdStringA &strDest)
473 {
474   CSingleLock lock(m_critSection);
475   if(!convert_checked(m_iconvUtf16LEtoUtf8,UTF8_DEST_MULTIPLIER,"UTF-16LE","UTF-8",strSource,strDest))
476     strDest.empty();
477 }
478
479 void CCharsetConverter::ucs2ToUTF8(const CStdString16& strSource, CStdStringA& strDest)
480 {
481   CSingleLock lock(m_critSection);
482   if(!convert_checked(m_iconvUcs2CharsetToUtf8,UTF8_DEST_MULTIPLIER,"UCS-2LE","UTF-8",strSource,strDest))
483     strDest.empty();
484 }
485
486 void CCharsetConverter::utf16LEtoW(const CStdString16& strSource, CStdStringW &strDest)
487 {
488   CSingleLock lock(m_critSection);
489   if(!convert_checked(m_iconvUtf16LEtoW,sizeof(wchar_t),"UTF-16LE",WCHAR_CHARSET,strSource,strDest))
490     strDest.empty();
491 }
492
493 void CCharsetConverter::ucs2CharsetToStringCharset(const CStdStringW& strSource, CStdStringA& strDest, bool swap)
494 {
495   CStdStringW strCopy = strSource;
496   if (swap)
497   {
498     char* s = (char*) strCopy.c_str();
499
500     while (*s || *(s + 1))
501     {
502       char c = *s;
503       *s = *(s + 1);
504       *(s + 1) = c;
505
506       s++;
507       s++;
508     }
509   }
510   CSingleLock lock(m_critSection);
511   convert(m_iconvUcs2CharsetToStringCharset,4,"UTF-16LE",
512           g_langInfo.GetGuiCharSet(),strCopy,strDest);
513 }
514
515 void CCharsetConverter::utf32ToStringCharset(const unsigned long* strSource, CStdStringA& strDest)
516 {
517   CSingleLock lock(m_critSection);
518
519   if (m_iconvUtf32ToStringCharset == (iconv_t) - 1)
520   {
521     CStdString strCharset=g_langInfo.GetGuiCharSet();
522     m_iconvUtf32ToStringCharset = iconv_open(strCharset.c_str(), "UTF-32LE");
523   }
524
525   if (m_iconvUtf32ToStringCharset != (iconv_t) - 1)
526   {
527     const unsigned long* ptr=strSource;
528     while (*ptr) ptr++;
529     const char* src = (const char*) strSource;
530     size_t inBytes = (ptr-strSource+1)*4;
531
532     char *dst = strDest.GetBuffer(inBytes);
533     size_t outBytes = inBytes;
534
535     if (iconv_const(m_iconvUtf32ToStringCharset, &src, &inBytes, &dst, &outBytes) == (size_t)-1)
536     {
537       CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
538       strDest.ReleaseBuffer();
539       strDest = (const char *)strSource;
540       return;
541     }
542
543     if (iconv(m_iconvUtf32ToStringCharset, NULL, NULL, &dst, &outBytes) == (size_t)-1)
544     {
545       CLog::Log(LOGERROR, "%s failed cleanup", __FUNCTION__);
546       strDest.ReleaseBuffer();
547       strDest = (const char *)strSource;
548       return;
549     }
550
551     strDest.ReleaseBuffer();
552   }
553 }
554
555 // Taken from RFC2640
556 bool CCharsetConverter::isValidUtf8(const char *buf, unsigned int len)
557 {
558   const unsigned char *endbuf = (unsigned char*)buf + len;
559   unsigned char byte2mask=0x00, c;
560   int trailing=0; // trailing (continuation) bytes to follow
561
562   while ((unsigned char*)buf != endbuf)
563   {
564     c = *buf++;
565     if (trailing)
566       if ((c & 0xc0) == 0x80) // does trailing byte follow UTF-8 format ?
567       {
568         if (byte2mask) // need to check 2nd byte for proper range
569         {
570           if (c & byte2mask) // are appropriate bits set ?
571             byte2mask = 0x00;
572           else
573             return false;
574         }
575         trailing--;
576       }
577       else
578         return 0;
579     else
580       if ((c & 0x80) == 0x00) continue; // valid 1-byte UTF-8
581       else if ((c & 0xe0) == 0xc0)      // valid 2-byte UTF-8
582         if (c & 0x1e)                   //is UTF-8 byte in proper range ?
583           trailing = 1;
584         else
585           return false;
586       else if ((c & 0xf0) == 0xe0)      // valid 3-byte UTF-8
587        {
588         if (!(c & 0x0f))                // is UTF-8 byte in proper range ?
589           byte2mask = 0x20;             // if not set mask
590         trailing = 2;                   // to check next byte
591       }
592       else if ((c & 0xf8) == 0xf0)      // valid 4-byte UTF-8
593       {
594         if (!(c & 0x07))                // is UTF-8 byte in proper range ?
595           byte2mask = 0x30;             // if not set mask
596         trailing = 3;                   // to check next byte
597       }
598       else if ((c & 0xfc) == 0xf8)      // valid 5-byte UTF-8
599       {
600         if (!(c & 0x03))                // is UTF-8 byte in proper range ?
601           byte2mask = 0x38;             // if not set mask
602         trailing = 4;                   // to check next byte
603       }
604       else if ((c & 0xfe) == 0xfc)      // valid 6-byte UTF-8
605       {
606         if (!(c & 0x01))                // is UTF-8 byte in proper range ?
607           byte2mask = 0x3c;             // if not set mask
608         trailing = 5;                   // to check next byte
609       }
610       else
611         return false;
612   }
613   return trailing == 0;
614 }
615
616 bool CCharsetConverter::isValidUtf8(const CStdString& str)
617 {
618   return isValidUtf8(str.c_str(), str.size());
619 }
620
621 void CCharsetConverter::utf8logicalToVisualBiDi(const CStdStringA& strSource, CStdStringA& strDest)
622 {
623   logicalToVisualBiDi(strSource, strDest, FRIBIDI_CHAR_SET_UTF8, FRIBIDI_TYPE_RTL);
624 }
625
626 CStdStringA CCharsetConverter::utf8Left(const CStdStringA &source, int num_chars)
627 {
628   CStdStringA result;
629   CStdStringW wide;
630   utf8ToW(source, wide);
631   wToUTF8(wide.Left(num_chars), result);
632   return result;
633 }