Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

古老的游戏为何会乱码(0):概述

这里是对古早Windows游戏乱码的小科普1,以帮助更多的朋友了解关于乱码的(有用的没用的)知识。也许我们有生之年能看到它的续集。

Windows 脚踏两条船

过去,Windows平台上的软件/小游戏开发主要基于原始的 Win32 及 GDI API。Win32 API负责与系统的所有交互,GDI 负责绘制图形。每个 Win32 API 及结构体 均有 A 和 W 两种版本。

Windows 系统提供两套字符结构,分别是以窄字节(char8_t,1字节长度)为基础类型的符合ANSI的窄字节字符串和以宽字节(char16_t,2字节长度;实际开发过程中使用wchar_t,Windows平台下通常认为是2字节长度)为类型的宽字节字符串。窄字节字符串又依据一个字符由多少字节表示分为单字节字符串与多字节字符串,它们均保证字符串中每个字节不为0x00。

CodePage 与 Win32 A

CodePage,又称代码页,是微软在 Windows 系列操作系统中为了实现全球化设计的方案。众所周知,人类使用的语言很多,每种语言对应的词库庞大,若设计对照表实现所有词汇与编码一一对应,将会对内存及存储产生较大压力。因此微软为不同地区及语言的用户设计了不同代码页,每个代码页仅存储了该地区/语言所需的词汇及编码数据,相同的编码数据在不同代码页中可能表示不同的字符;中国大陆地区常用的简体中文代码页在 Windows 中的编号为 936,其中包含了GBK的所有字及其编码。代码页 + 窄字节字符串 共同实现了早期 Windows 中的全球化语言处理。在 Win32 API中,以 A 结尾的函数或结构体表明其应用于 代码页 + 窄字节字符串 的运行环境。

Unicode 与 Win32 W

代码页的方案带来的劣势显而易见:一个应用程序在同一时间只能拥有一个代码页,在处理不同语言数据时常常遇到困难。微软随后试图抛弃代码页,采用 Unicode 的统一编码(微软自己叫它UCS-2,有时又喊它UCS16),它以2字节(16比特)的宽字节为基础单元,尽可能将人类常用的字符映射到一个基础单元中,不常见或特殊的字符则使用更多的基础单元表示。在 Win32 API中,以 W 结尾的函数或结构体表明其应用于 Unicode 的运行环境。

为何乱码?

古早/使用老引擎或框架制作的 Windows 游戏常常采用了前述第一种 代码页 + 窄字节字符串 的方案,这便是乱码的根源。

首先要从 Windows 内部如何处理 A 和 W 两种不同的 Win32 API 调用讲起。Windows 内部代码已实现完整 Unicode 化,应用程序的所有 W 版本 Win32 API 可被直接处理;而 A 版本 Win32 API 则是经过参数转换后间接调用其对应的 W 类型 API 实现的,参数转换即是将 A 版本所用的 代码页 + 窄字节字符串 转换为 W 版本所用的 宽字节 Unicode 字符串。

问题便出现在这个转换的过程中: 窄字节字符串的内容是确定的,可系统如何确定代码页是多少,例如某个古老的游戏应当运行于简体中文的代码页还是日语的代码页?Windows 不知道,也不关心。代码页是系统全局层面的设置,在绝大多数情况下,操作系统中的每个应用都使用与系统相同的代码页,该信息可由 GetACP 函数获取。

于是我们便可以料想,日语代码页(932)编写的古早游戏/软件,自然无法在简体中文代码页(936)下正常处理绝大部分字符串,在图形界面以乱码的形式展示。有些软件放在全英文的目录下才可运行,若有中文路径则闪退;同时英文的字符都可以正常显示,这是为什么呢?这是因为,即使是不同的代码页,0x00–0x7F这段区间内都与ASCII兼容(除去极少部分特例代码页),这段编码对应于英文、数字及常用标点符号,在绝大多数环境下均可被正常处理。

如何解决?

绝大部分软件仅面向与自己使用相同语言/在相同地域的用户群体,对开发者来说特意将古老的代码拉出来把 A 版本 API换为 W 版本 API 实在缺乏性价比。

一个替代方案是使用 Locale Emulator, 它利用 DLL 注入劫持目标程序,「诱骗」其在合适的代码页中工作。如前文所述,如此一来程序中 A 版本 到 W 版本 API 参数的转换便有了正确的代码页信息,能解决大部分有关编码的问题。(其中的「中文简体,中国大陆」对应简中CP936,日本对应CP932)


  1. 有关Windows UTF-8 的内容暂未提及;本文对部分概念和功能进行了简化/笼统表述以避免可能出现的阅读困难。