在汉化以及开发国际化软件的过程中很难避免各种字符编码转换的问题。相信各位都在Windows98上玩过以前的轩辕剑、仙剑等台湾出品的游戏,而且对于那不用南极星打开游戏就会乱码的印象一定非常深刻。
在计算机诞生的时候,由于没考虑到计算机会被这么广泛地使用,于是就采用只能表示127个字符的ASCII码。而到了后来,各个国家就相继开发出各自的编码标准。而对于咱们中国人来说,最熟悉的莫过于是GBK编码以及BIG-5编码了,当然还有日文的Shift-JIS编码。这些编码方式都称为双字节字符编码(double-byte character set)。
这篇东西不打算细述各种编码的历史跟编码方法,对此有兴趣的可以自行上网搜索。我在这里就简单说说产生乱码的原因。
拿Shift-JIS编码来说,对于以932代码页为默认代码页的系统来说,当我输入了“あのそら”这句句子保存后,用WinHex等东西打开可以看到各个字符的编码分别是82 A0 82 CC 82 BB 82 E7,这些字符就是Shift-JIS编码,在日文的系统中能识别出正确的字符,但到了中文的系统就将其当成是GBK编码的东西来解析,于是就变成了“偁偺偦傜”这样的东西了。
为了解决这个问题,微软提供了一个叫APP的东西,用来转换区域让程序不会显示乱码。而这个东西的原理十分简单,就是将__COMPAT_LAYER 和 ApplocaleID这两个环境变量改成目标区域的代码再运行程序。
而另一个著名的转区软件NTLEA则是采用了HOOK相关字符串处理的函数来实现区域转换(实际上这个软件的早期版本是开源的,但现在在网上都找不到源代码,只好拿IDA来逆向那个关键的DLL了)。NTLEA几乎挂钩了所有跟字符串有关的API,包括CreateWindowExA等创建窗口的函数甚至是DialogBoxA都不放过,这些挂钩的函数大多数都是将传入的字符串用MultiByteToWideChar强行按照设定的代码页转换成宽字符再调用对样函数的宽字符版本。另外所有跟区域代码页等有关的函数也全部挂钩,更改其返回值。这种很黄很暴力的方法可是比APP有效得多。但这有一个缺点,当用NTLEA加载一些加了壳的游戏的时候,会被当成是用调试器加载直接退出。
上面的解决方法都是治标不治本,最根本的方法还是利用Unicode编码来开发程序。(但对于我这种懒人来说在每个字符串前面加个“L”可是一件很大工作量的事情……)
有了上面的东西现在就来说一下游戏汉化中遇到的字符边界检查的问题。
现在很多GALGAME输出文字都是使用GetGlyphOutline函数来实现的。这个函数每次传入一个字符的参数,将其转换成矩阵,再利用BitBlt等函数来绘图输出文字。然而,每一种编码方式都是有编码范围的,例如日语的编码范围就是从0x80~0xA0以及0xE0~0xFC(双字中的低字节)。为了避免输入无效的字符造成程序出错,一般在调用GetGlyphOutline函数前都会有如下一段检查的代码:
引用
cmp al, 80
jbe short 0040676F
cmp al, 0A0
jb short 00406777
cmp al, 0E0
jb short 004067D0
cmp al, 0FC
ja short 004067D0
当传入的参数是0xA0~0xE0之间的值的时候就代表这是无效的编码,函数直接跳走处理。
然而,对于中文来说,GBK编码的范围比日文大得多,假如汉化的时候不修改上述的判断的时候会让程序误以为传入的是无效的字符而将原来的字符编码打乱,造成乱码。解决这个方法只需将判断边界的代码中0xA0以及0xFC改成GBK编码的上界0xFE即可。
分享到:
相关推荐
字符编码是计算机处理文本的基础,它定义了字符与二进制数字之间的对应关系。本文将深入探讨ASCII、Unicode以及UTF-8三种字符编码系统,帮助理解它们的关系和各自的特点。 首先,ASCII(American Standard Code for...
- **内外划分**:在Java中,字符编码转换主要发生在两个层面之间——JVM内部与操作系统(OS)的文件系统之间。 - **JVM内部**:所有字符均以UTF-16格式存储。 - **OS文件系统**:字符可能以不同的编码格式存在于...
5. **处理宽字符与窄字符**:在某些编码中,比如GBK,有些中文字符是两个字节表示的,称为宽字符;而在UTF-8中,每个中文字符是三个或四个字节。截取时要注意宽字符的边界,防止截断中间。 6. **边界判断**:在截取...
在这个"matlab开发-算术编码字符串"项目中,我们将深入探讨算术编码的概念、原理以及在MATLAB环境中的实现。 算术编码的基本思想是将每个字符或符号映射到一个概率区间,这个区间长度与该符号出现的概率成反比。...
Delphi提供了`TEncoding`类来处理不同的字符编码。 最后,关于提供的压缩包文件,它们可能是Delphi项目的配置文件(.cfg、.dpr、.dof等)、单元文件(.dfm、.pas)以及资源文件(.res)。这些文件包含了项目的设置...
3. 处理边界情况:检查是否处理了分隔符出现在字符串开头或结尾的情况,以及连续的分隔符。 七、进一步学习 除了基础的“字符串到数组”函数,LabVIEW还提供了其他字符串处理函数,如“查找子字符串”、“替换子...
首先,我们需要了解易语言中的字符串处理和随机数生成。易语言提供了“字符串”类型来表示字符序列,可以通过“取字符串长度”函数获取字符串的长度,通过“连接”函数拼接字符串,通过“取子字符串”函数截取字符串...
相对论开放字符串被认为是在平面近似中以规范理论对局限夸克-反夸克对的动力学进行编码。 Neumann边界算子可以通过在目标空间坐标Xμ的缩放下的行为来组织,并且所允许的X缩放指数集在上方为+1 / 2且在下方为无界。...
- **哈夫曼编码**:是数据压缩中的一种算法,通过优化字符编码来提高压缩效率。 8. **实际应用**: - 哈夫曼编码常用于文本、图像和音频等数据的压缩,如ZIP、GIF和MP3格式的文件压缩就利用了类似的思想。 - 在...
在C++编程中,处理字符串是一项常见的任务,特别是在游戏开发客户端和服务器端的场景中。题目中的问题聚焦于如何正确地截取一个中英混合的字符串,确保在截取过程中不会将汉字字符截断。这个问题涉及到字符编码、...
每次移动右边界时,我们检查新加入的字符是否已经在哈希集合中。如果不在,我们就更新窗口的长度;如果在,我们就收缩左边界并移除旧的字符。在整个过程中,我们需要记录并更新最大窗口长度。 以下是可能的 C++ ...
中英文字符串的切割边界的确定算法 >> 一些背景知识: 1. 一个汉字在c\c++的存储, 使用2个字节(char)存储; 2. 汉字存储的第一个char, 其值一定大于'~'(0111 1110=126),否则将导致识别歧义; 此处, 使用"单ASCII...
另外,为了确保整个程序的兼容性,应该在项目开始时设定统一的字符编码标准,并在整个代码中保持一致。在HTML文档中,可以使用`<meta charset="utf-8">`来声明文档的字符编码。 总的来说,理解和正确处理UTF-8格式...
总结来说,字符集和编码是处理文本的基础,Unicode字符集提供了一种全球通用的标准,而UTF-8编码则是一种高效且广泛使用的变长编码方式,能够有效地解决字符边界识别和内存利用的问题。在编程和数据处理领域,理解和...
1. **编码问题**:在处理包含中文字符的字符串时,确保整个程序使用了正确的字符编码(如UTF-8)。 2. **边界条件处理**:当字符串长度小于或等于指定的字节数时,需要特别处理,避免数组越界等异常。 3. **性能考虑...
综上所述,VB过滤中文字符串主要涉及字符串处理、字符编码检查以及可能的正则表达式操作。根据具体需求,你可以选择适合的方法实现字符串的过滤功能。在开发过程中,确保对各种边界条件和异常情况进行充分测试,以...
在处理短信服务相关的程序时,理解GSM编码和如何与其它字符集转换是非常关键的。 总之,理解字符集和字符串转化方法对于VC++开发者来说是基础但必不可少的技能。在实际开发中,我们需要根据需求选择合适的字符集和...
在C语言中,字符转换通常涉及到字符编码、ASCII码、Unicode等概念。我们需要理解字符在内存中的表示方式,以及如何通过编程来改变这些表示。以下是一些可能涉及的关键知识点: 1. **字符编码**:在C语言中,字符...
为了确保完整性,通常需要以字符边界进行截取,这可能需要借助于`codecs`库的`decode()`和`iterdecode()`等方法。 在Java中,我们可以使用`substring()`方法结合`getBytes()`来实现。但是,Java的字符串是Unicode的...
当使用`substr()`或`substring()`截取中文字符串时,如果截取的起始位置和长度没有正确地对齐到字符边界,就可能导致部分字符的字节被截断,从而引发乱码。这种问题在其他数据库系统中也常见,尤其是那些不支持...