现在的位置:首页 > 脚本相关(C++) >

MANGOS中文问题最懒解决方案


tyre发布于2008-01-27 04:11:43 芒果中文

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

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

目前的做法:

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

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

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

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

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

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

#include "stdafx.h"
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;

void UTF_8ToUnicode(wchar_t* pOut,char *pText)
{
    char* uchar = (char *)pOut;

    uchar[1] = ((pText[0] & 0x0F) << 4) + ((pText[1] >> 2) & 0x0F);
    uchar[0] = ((pText[1] & 0x03) << 6) + (pText[2] & 0x3F);

    return;
}

void UnicodeToUTF_8(char* pOut,wchar_t* pText)
{
    char* pchar = (char *)pText;

    pOut[0] = (0xE0 | ((pchar[1] & 0xF0) >> 4));
    pOut[1] = (0x80 | ((pchar[1] & 0x0F) << 2)) + ((pchar[0] & 0xC0) >> 6);
    pOut[2] = (0x80 | (pchar[0] & 0x3F));

    return;
}

void UnicodeToGB2312(char* pOut,wchar_t uData)
{
    WideCharToMultiByte(CP_ACP,NULL,&uData,1,pOut,sizeof(wchar_t),NULL,NULL);
    return;
}

void Gb2312ToUnicode(wchar_t* pOut,char *gbBuffer)
{
    MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,gbBuffer,2,pOut,1);
    return ;
}

void GB2312ToUTF_8(string& pOut,char *pText, int pLen)
{
    char buf[4];
    int nLength = pLen* 3;
    char* rst = new char[nLength];

    memset(buf,0,4);
    memset(rst,0,nLength);

    int i = 0;
    int j = 0;
    while(i < pLen)
    {
        if( *(pText + i) >= 0)
        {
            rst[j++] = pText[i++];
        }
        else
        {
            wchar_t pbuffer;
            Gb2312ToUnicode(&pbuffer,pText+i);

            UnicodeToUTF_8(buf,&pbuffer);

            unsigned short int tmp = 0;
            tmp = rst[j] = buf[0];
            tmp = rst[j+1] = buf[1];
            tmp = rst[j+2] = buf[2];

            j += 3;
            i += 2;
        }
    }
    rst[j] = '\0';

    pOut = rst;
    delete []rst;

    return;
}

void UTF_8ToGB2312(string &pOut, char *pText, int pLen)
{
    char * newBuf = new char[pLen];
    char Ctemp[4];
    memset(Ctemp,0,4);

    int i =0;
    int j = 0;

    while(i < pLen)
    {
        if(pText > 0)
        {
            newBuf[j++] = pText[i++];
        }
        else
        {
            WCHAR Wtemp;
            UTF_8ToUnicode(&Wtemp,pText + i);

            UnicodeToGB2312(Ctemp,Wtemp);

            newBuf[j] = Ctemp[0];
            newBuf[j + 1] = Ctemp[1];

            i += 3;
            j += 2;
        }
    }
    newBuf[j] = '\0';

    pOut = newBuf;
    delete []newBuf;

    return;
}

int _tmain(int argc, _TCHAR* argv[])
{
    string strIN = " 管理员 ";
    cout << "init 0 : " << strIN << endl;
    string strOUT;
    cout << ". GB2312 -> UTF-8  ." << endl;
    char *chr1 = (char *)strIN.c_str();
    GB2312ToUTF_8(strOUT, chr1, strlen(chr1));
    cout << strOUT << endl;
    cout << ". UTF-8  -> GB2312 ." << endl;
    // 测试1 (直接在代码中转换并传递)
    char *chr2 = (char *)strOUT.c_str();
    UTF_8ToGB2312(strOUT, chr2, strlen(chr2));
    cout << "test 1 : " << strOUT << endl;
    // 测试2 (外部工具转换并放入源码)
    char *chr3 = " 绠$悊鍛 ";
    UTF_8ToGB2312(strOUT, chr3, strlen(chr3));
    cout << "test 2 : " << strOUT << endl;
    return 0;
}

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

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

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

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

----------------------------------------
...

void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
{
    WorldPacket data( SMSG_GOSSIP_MESSAGE, (100) );
    data << npcGUID;
    data << uint32( TitleTextId );
    data << uint32( pGossipMenu->MenuItemCount() );

    for ( unsigned int iI = 0; iI < pGossipMenu->MenuItemCount(); iI++ )
    {
        data << uint32( iI );
        data << uint8( pGossipMenu->GetItem(iI).m_gIcon );
        data << uint8( pGossipMenu->GetItem(iI).m_gCoded );
        data << uint32(0);


        // Tyre 解决VS2005中文问题 ->
        std::string str = pGossipMenu->GetItem(iI).m_gMessage;
        if(str[0] == ' '){ // 根据首字符是否为空格来判断是否需要转换
            char *chr = (char *)str.c_str();
            TyreCodeTransformer::GB2312ToUTF_8(str, chr, strlen(chr));
        }
        data << str;
        // <- Tyre


        data << uint8(0);
    }

    data << uint32( pQuestMenu->MenuItemCount() );

    for ( uint16 iI = 0; iI < pQuestMenu->MenuItemCount(); iI++ )
    {
        uint32 questID = pQuestMenu->GetItem(iI).m_qId;
        Quest const* pQuest = objmgr.GetQuestTemplate(questID);

        data << questID;
        data << uint32( pQuestMenu->GetItem(iI).m_qIcon );
        data << uint32( pQuest ? pQuest->GetQuestLevel() : 0 );
        std::string Title = pQuest->GetTitle();
        if (pSession->GetSessionLanguage()>0)
        {
            QuestLocale const *ql = objmgr.GetQuestLocale(questID);
            if (ql)
            {
                if (ql->Title[pSession->GetSessionLanguage()]!="")
                    Title=ql->Title[pSession->GetSessionLanguage()];
            }
        }
        data << Title;
    }

    pSession->SendPacket( &data );
}

...

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

大家可能注意到了

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

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

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

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

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

总结最懒解决方案:

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

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







     声明:本网站部分资源来自互联网,如果您认为侵犯了您的版权,您可以联系我们,我们将在第一时间删除相关内容。
     本站立足开源项目Mangos研究,如果您对此有兴趣,请加入我们。本站QQ群:14646754
     本群拒绝脑残体,脑残体患者、乱码字符ID请在进群后自行更改群名片,否则将视作定期清理对象。