`
finalbone
  • 浏览: 55675 次
  • 性别: Icon_minigender_1
  • 来自: 海边
社区版块
存档分类
最新评论

MANGOS中文问题最懒解决方案

阅读更多

 

MANGOS的程序架构没有考虑多语种支持,给我们带来了不少麻烦~

----------------------------------------

目前的做法:

汉化源码->记事本转存UTF-8->字符串首尾加空格(UTF-8格式保存)->VS2003编译->中文完美显示

此方法只适用于VS2003 (VS2005中文无法完整显示),操作繁琐,转化后的源码不直观不便修改...

----------------------------------------

本人对C++不太熟,经过一段时间摸索,找到了一个解决此问题的方法,而且对已完成的汉化内容不用做任何修改即可生效,暂且叫最懒解决方案吧 --__--|||

我发现MANGOS向客户端发送文本的都是UTF-8编码的,经过调试发现VS2005编译后的源码在发送前就已经存在问题了,也就是说用VS2005编译的含有外部工具转换后的UTF-8编码的中文字符的源码并不能保证中文部分的完整性,具体内在原因我没有再深入查下去...就算查下去也不太可能修改VS2005的编译方式~~~

验证此问题可用如下代码:

cpp 代码
  1. #include "stdafx.h"   
  2. #include <iostream>
    #include <string>
    #include <windows.h>
    using namespace std;
       
  3.   
  4. void UTF_8ToUnicode(wchar_t* pOut,char *pText)   
  5. {   
  6.     char* uchar = (char *)pOut;   
  7.   
  8.     uchar[1] = ((pText[0] & 0x0F) << 4) + ((pText[1] >> 2) & 0x0F);   
  9.     uchar[0] = ((pText[1] & 0x03) << 6) + (pText[2] & 0x3F);   
  10.   
  11.     return;   
  12. }   
  13.   
  14. void UnicodeToUTF_8(char* pOut,wchar_t* pText)   
  15. {   
  16.     char* pchar = (char *)pText;   
  17.   
  18.     pOut[0] = (0xE0 | ((pchar[1] & 0xF0) >> 4));   
  19.     pOut[1] = (0x80 | ((pchar[1] & 0x0F) << 2)) + ((pchar[0] & 0xC0) >> 6);   
  20.     pOut[2] = (0x80 | (pchar[0] & 0x3F));   
  21.   
  22.     return;   
  23. }   
  24.   
  25. void UnicodeToGB2312(char* pOut,wchar_t uData)   
  26. {   
  27.     WideCharToMultiByte(CP_ACP,NULL,&uData,1,pOut,sizeof(wchar_t),NULL,NULL);   
  28.     return;   
  29. }   
  30.   
  31. void Gb2312ToUnicode(wchar_t* pOut,char *gbBuffer)   
  32. {   
  33.     MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,gbBuffer,2,pOut,1);   
  34.     return ;   
  35. }   
  36.   
  37. void GB2312ToUTF_8(string& pOut,char *pText, int pLen)   
  38. {   
  39.     char buf[4];   
  40.     int nLength = pLen* 3;   
  41.     char* rst = new char[nLength];   
  42.   
  43.     memset(buf,0,4);   
  44.     memset(rst,0,nLength);   
  45.   
  46.     int i = 0;   
  47.     int j = 0;   
  48.     while(i < pLen)   
  49.     {   
  50.         if( *(pText + i) >= 0)   
  51.         {   
  52.             rst[j++] = pText[i++];   
  53.         }   
  54.         else  
  55.         {   
  56.             wchar_t pbuffer;   
  57.             Gb2312ToUnicode(&pbuffer,pText+i);   
  58.   
  59.             UnicodeToUTF_8(buf,&pbuffer);   
  60.   
  61.             unsigned short int tmp = 0;   
  62.             tmp = rst[j] = buf[0];   
  63.             tmp = rst[j+1] = buf[1];   
  64.             tmp = rst[j+2] = buf[2];   
  65.   
  66.             j += 3;   
  67.             i += 2;   
  68.         }   
  69.     }   
  70.     rst[j] = '\0';   
  71.   
  72.     pOut = rst;   
  73.     delete []rst;   
  74.   
  75.     return;   
  76. }   
  77.   
  78. void UTF_8ToGB2312(string &pOut, char *pText, int pLen)   
  79. {   
  80.     char * newBuf = new char[pLen];   
  81.     char Ctemp[4];   
  82.     memset(Ctemp,0,4);   
  83.   
  84.     int i =0;   
  85.     int j = 0;   
  86.   
  87.     while(i < pLen)   
  88.     {   
  89.         if(pText[i] > 0)   
  90.         {   
  91.             newBuf[j++] = pText[i++];   
  92.         }   
  93.         else  
  94.         {   
  95.             WCHAR Wtemp;   
  96.             UTF_8ToUnicode(&Wtemp,pText + i);   
  97.   
  98.             UnicodeToGB2312(Ctemp,Wtemp);   
  99.   
  100.             newBuf[j] = Ctemp[0];   
  101.             newBuf[j + 1] = Ctemp[1];   
  102.   
  103.             i += 3;   
  104.             j += 2;   
  105.         }   
  106.     }   
  107.     newBuf[j] = '\0';   
  108.   
  109.     pOut = newBuf;   
  110.     delete []newBuf;   
  111.   
  112.     return;   
  113. }   
  114.   
  115. int _tmain(int argc, _TCHAR* argv[])   
  116. {   
  117.     string strIN = " 管理员 ";   
  118.     cout << "init 0 : " << strIN << endl;   
  119.     string strOUT;   
  120.     cout << ". GB2312 -> UTF-8  ." << endl;   
  121.     char *chr1 = (char *)strIN.c_str();   
  122.     GB2312ToUTF_8(strOUT, chr1, strlen(chr1));   
  123.     cout << strOUT << endl;   
  124.     cout << ". UTF-8  -> GB2312 ." << endl;   
  125.     // 测试1 (直接在代码中转换并传递)   
  126.     char *chr2 = (char *)strOUT.c_str();   
  127.     UTF_8ToGB2312(strOUT, chr2, strlen(chr2));   
  128.     cout << "test 1 : " << strOUT << endl;   
  129.     // 测试2 (外部工具转换并放入源码)   
  130.     char *chr3 = " 绠$悊鍛 ";   
  131.     UTF_8ToGB2312(strOUT, chr3, strlen(chr3));   
  132.     cout << "test 2 : " << strOUT << endl;   
  133.     return 0;   
  134. }   

VS2005编译后 test 2 的输出结果并不是“管理员” --__--|||

----------------------------------------

问题找到了,接下来就是解决问题,既然VS2005不能完美编译预先转换后的中文源码,那就改写这部分的处理方式即可。

顺着这条思路我找到了GossipDef.cpp的void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID)方法,这是MANGOS服务端向客户端输出对话界面的最终出口,在这里做一些改写即可达到目的,改写后的代码如下:

----------------------------------------

cpp 代码
  1. ...   
  2.   
  3. void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )   
  4. {   
  5.     WorldPacket data( SMSG_GOSSIP_MESSAGE, (100) );   
  6.     data << npcGUID;   
  7.     data << uint32( TitleTextId );   
  8.     data << uint32( pGossipMenu->MenuItemCount() );   
  9.   
  10.     for ( unsigned int iI = 0; iI < pGossipMenu->MenuItemCount(); iI++ )   
  11.     {   
  12.         data << uint32( iI );   
  13.         data << uint8( pGossipMenu->GetItem(iI).m_gIcon );   
  14.         data << uint8( pGossipMenu->GetItem(iI).m_gCoded );   
  15.         data << uint32(0);   
  16.   
  17.   
  18.         // Tyre 解决VS2005中文问题 >>>   
  19.         std::string str = pGossipMenu->GetItem(iI).m_gMessage;   
  20.         if(str[0] == ' '){ // 根据首字符是否为空格来判断是否需要转换   
  21.             char *chr = (char *)str.c_str();   
  22.             TyreCodeTransformer::GB2312ToUTF_8(str, chr, strlen(chr));   
  23.         }   
  24.         data << str;   
  25.         // <<< Tyre   
  26.   
  27.   
  28.         data << uint8(0);   
  29.     }   
  30.   
  31.     data << uint32( pQuestMenu->MenuItemCount() );   
  32.   
  33.     for ( uint16 iI = 0; iI < pQuestMenu->MenuItemCount(); iI++ )   
  34.     {   
  35.         uint32 questID = pQuestMenu->GetItem(iI).m_qId;   
  36.         Quest const* pQuest = objmgr.GetQuestTemplate(questID);   
  37.   
  38.         data << questID;   
  39.         data << uint32( pQuestMenu->GetItem(iI).m_qIcon );   
  40.         data << uint32( pQuest ? pQuest->GetQuestLevel() : 0 );   
  41.         std::string Title = pQuest->GetTitle();   
  42.         if (pSession->GetSessionLanguage()>0)   
  43.         {   
  44.             QuestLocale const *ql = objmgr.GetQuestLocale(questID);   
  45.             if (ql)   
  46.             {   
  47.                 if (ql->Title[pSession->GetSessionLanguage()]!="")   
  48.                     Title=ql->Title[pSession->GetSessionLanguage()];   
  49.             }   
  50.         }   
  51.         data << Title;   
  52.     }   
  53.   
  54.     pSession->SendPacket( &data );   
  55. }   
  56.   
  57. ...  

----------------------------------------

大家可能注意到了

if(str[0] == ' '){ // 根据首字符是否为空格来判断是否需要转换

因为PlayerMenu::SendGossipMenu是处理源码与数据库中数据的统一出口,对于原本就是UTF-8编码的数据不能进行再次转换,不然会产生乱码。为了便于区分加上根据之前的习惯,汉化后的字符串前后都已经加上了空格,根据这一特点决定了用此方式来区分是否需要转换,也可以换种方式加上字符串编码分析然后再做区分判断(可扩展为多语种支持),暂时不搞了,以后再说吧~

此方式通过扩展是可以支持多语种的,但是将内容文本HardCode进源码的味道很不好~,还是等待MANGOS团队对代码做整体更新吧,另外MANGOS的文本输出接口可能不只这一个PlayerMenu::SendGossipMenu,等发现了其它接口用同样方法修改即可。。。

呵呵!不求最好!只求最懒!如果有更好的方法请大家和我联系 QQ 15149077 泛开源技术研究群 40635510 !

----------------------------------------

总结最懒解决方案:

汉化源码->字符串首加空格(GB2312/UTF-8格式保存)->VS2003/2005编译->中文完美显示

----------------------------------------

不知道MANGOS是什么? 去这里看看就知道了 http://www.mangosproject.org

----------------------------------------

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics