欧陆风云4吧 关注:360,412贴子:11,002,412
  • 16回复贴,共1

mac中文双字节补丁开发,基本成功(还没封装)

只看楼主收藏回复

接上文mac版本中文双字节补丁,ai开发中(...,在codex日夜不停的工作下,在我“辛苦地”刷完了b站五代十国、南北朝视频后,终于成功把选项字体、长文本、tooltip、动态地图全部在macos下显示了中文


IP属地:北京来自iPhone客户端1楼2026-05-16 19:23回复
    由于我一个字的代码都没写,所以需要gpt5.5亲自说他干了什么


    IP属地:北京来自iPhone客户端3楼2026-05-16 19:30
    回复
      2026-06-07 13:40:17
      广告
      不感兴趣
      开通SVIP免广告
      具体的实行方案:
      EU4本身没有中文支持,很多文本处理逻辑仍然停留在单字节字符模型里:一个字符就是一个byte,字符ID也基本被限制在0-255。中文显然不适配这个模型。所以Windows版双字节补丁的基本思路,是把中文用特殊转义字节序列表示,再在游戏运行时改写字体、宽度、渲染等逻辑,让这些序列最终能变成中文字形。
      macOS版不能直接用WindowsDLL,所以这次是在macOS原生版EU4上重新做了一套运行时补丁:通过`dylib`注入,hook游戏里的`CBitmapFont`、地图文字、纹理加载等关键函数,尽量保持汉化mod本体不变。
      **普通UI文本**
      普通UI的第一关是让游戏“看得懂”汉化mod里的双字节序列。
      汉化文本里,一个中文字符不是直接以UTF-8交给EU4,而是编码成特殊的多字节形式。补丁会在字体渲染路径里拦截文本,识别这些双字节中文序列,解码出真实的Unicode码点。
      但EU4内部很多地方并不能直接处理大于255的字符ID。于是这里用了一个“代理字符槽”的办法:
      -先把原始双字节文本解码成中文码点;
      -再从对应的`.fnt`字体文件sidecar里查找这个汉字的glyph信息;
      -然后临时把这些中文字形塞进0-255范围内的可用槽位;
      -最后把原文本替换成一串代理字符,让EU4以为自己还在画普通单字节字符。
      也就是说,游戏内部看到的是“普通字符”,但这些字符槽实际指向的是中文字形。这样可以绕过EU4对字符ID的限制,同时不需要改汉化mod原始文本。
      **字体sidecar和glyph映射**
      EU4的bitmapfont使用`.fnt+.dds`结构。`.fnt`里记录每个字符的:
      -字符ID;
      -glyph在贴图atlas里的x/y/w/h;
      -xoffset/yoffset;
      -xadvance;
      -page/channel等信息。
      补丁会读取中文字体的`.fnt`sidecar,建立一个查询表。渲染时遇到中文码点,就能找到对应glyph,并把glyph的坐标、宽度、高度、advance等信息复制到EU4运行时正在使用的字体结构里。
      这一层很关键,因为中文不是简单“换字体”就能解决。游戏必须在算宽度、排版、写顶点、取UV时都拿到同一套glyph数据,否则就会出现能显示但错位、能排版但贴图错、或者tooltip一字一行的问题。
      **字宽、按钮和tooltip**
      普通文本能显示之后,第二个大坑是“测量”。
      EU4在渲染前会频繁调用类似`GetWidthOfString`、`GetRequiredSize`的函数。按钮、列表、tooltip、长文本框都会依赖这些测量结果。如果游戏仍然按原始byte数量去算中文宽度,就会出问题:
      -tooltip宽度被算错;
      -中文可能一字一行;
      -按钮文字被截断;
      -文本框高度不对;
      -长文本换行位置混乱。
      所以补丁不只hook渲染,还hook了宽度计算和requiredsize相关路径。核心原则是:**渲染用什么glyph,测量也必须用同一套glyph**。
      这部分做法大致是:
      -拦截宽度计算;
      -解码双字节中文;
      -用sidecar里的`xadvance`计算真实中文宽度;
      -对特殊UI场景做保护,避免把原生英文、数字、控制符、图标文本误处理;
      -对tooltip做窄范围修正,避免影响其他界面。
      tooltip之所以特别麻烦,是因为它既依赖宽度,又依赖换行,还经常动态生成,文本来源复杂。早期很容易出现“一字一行”“只显示半个字”“末尾乱码”等问题。最后稳定方案是把tooltip和长文本的修正范围收窄,只在确定需要中文处理的路径上启用,避免全局粗暴替换。
      **长文本和中文换行**
      国家介绍、剧本说明、教程文本、事件描述这类长文本,又是另一类问题。
      EU4原始换行逻辑基本是按字节扫描。中文双字节序列如果被它从中间切开,就会出现:
      -一个汉字被拆成两半;
      -下一行开头出现残缺字节;
      -文本宽度突然异常;
      -后续一整段都被污染。
      所以补丁给长文本路径加了CJK安全换行逻辑。这里没有简单全局替换换行算法,而是根据调用栈、字体大小、文本框矩形、文本长度等特征,识别几个稳定场景:
      在这些场景里,补丁会避免在中文转义序列内部断行,让换行发生在完整中文字符边界上。这样既保住了长文本可读性,也避免影响按钮、短标签、地图等不该套用长文本逻辑的地方。


      IP属地:北京5楼2026-05-16 19:40
      回复
        地图地名是这次 macOS 汉化补丁里最难的一部分。它和普通 UI 文本不是同一条渲染路径。普通按钮、面板、说明文字大多走CBitmapFont的屏幕文字渲染逻辑,而地图国家名、省份名这类文字要沿着地图区域弯曲、缩放、旋转,所以会进入另一套地图文字管线,大致是:
        RenderNames
        -> CBitmapFont::GetFontTexture
        -> CTextureHandler::GetTexture
        -> FillVertexBuffer
        -> CurveText
        -> GfxSetTextures
        一开始地图上出现的是大片白块,而且基本是一字一块,比如“奥斯曼”对应三个白块。白块跟着地图 label 的曲线变化,也说明它已经被当成地图文字 quad 画出来了,只是采样不到正确的字体贴图。
        排查时先确认了几个环节。第一,地图文本确实按中文字符进入了FillVertexBuffer和CurveText。第二,中文 glyph 能从zh-hans-map.fnt找到,missing_glyphs=0。第三,生成的 glyph quad 数量和中文字符数匹配。第四,通过反汇编确认FillVertexBuffer的顶点结构,UV 写入位置是可信的。第五,CurveText虽然内部仍然是按 byte 查表,但补丁已经通过代理字符槽把中文 glyph 映射进 0-255 范围,所以它能拿到有效 glyph。换句话说,中文解码、glyph 查询、顶点数量、UV、曲线排版这些最容易怀疑的地方,最后都不是根因。
        真正的问题出现在最后的纹理绑定链路。地图渲染时,RenderNames会先调用 `CBitmapFont
        ::GetFontTexture()取得字体贴图 handle,再通过CTextureHandler
        ::GetTexture(handle)拿到真正的TextureGFX*,最后交给GfxSetTextures绑定到渲染管线。日志显示,地图字体对象是对的,GetFontTexture()返回的 handle 也是对的,值为2,并且字体指标匹配zh-hans-map.fnt。但下一步CTextureHandler
        ::GetTexture(2)返回的是空指针,也就是TextureGFX*=0x0`。这就解释了为什么会显示白块:文字、顶点和 UV 都准备好了,但渲染时没有绑定到有效的中文地图字体贴图,所以每个 glyph quad 只能被画成白色块。
        接下来继续追 `CTextureHandler
        ::LoadTexture。一开始怀疑是不是zh-hans-map.dds文件找不到,或者路径从.tga推导到.dds时失败。但进一步日志确认,zh-hans-map.dds在 VFS 里存在,VFSExists("gfx/fonts/zh-hans-map.dds") = true,文件对象也能成功打开,CVirtualFile
        ::IsValid() = true`。这说明问题不是文件不存在,也不是虚拟文件系统看不到它。
        真正卡住的是一个非常隐蔽的大小限制。macOS 版 EU4 的 `CTextureHandler
        ::LoadTexture里有两处0xffffff的文件大小上限,也就是大约 16MB。而中文地图字体贴图zh-hans-map.dds的实际大小是49049728` 字节,约 49MB。加载过程因此变成:
        VFSExists = true
        CVirtualFile::IsValid = true
        GetSize = 49049728
        size > 0xffffff
        直接退出
        没有 Read
        没有 LoadTextureFromMemory
        没有 TextureGFX*
        地图显示白块
        也就是说,游戏明明找到了文件,也能打开文件,但因为文件超过内部上限,它在真正读取 DDS 数据之前就退出了。这个点解释了之前所有现象:glyph 有、UV 有、quad 有,但最终没有贴图。
        最终补丁做了两个关键修正。第一,把地图字体贴图从游戏内部默认推导的.tga路线导向实际存在的zh-hans-map.dds。第二,把 `CTextureHandler::LoadTexture` 里的两处 16MB 上限提高到 128MB,让 49MB 的中文地图 atlas 可以继续进入读取和解码流程。DDS 已经成功读入内存、成功解码,并生成了有效的 GPU 纹理对象。此后 `CTextureHandler
        ::GetTexture(2)不再返回空指针,GfxSetTextures` 能绑定正确的地图字体 atlas,地图上的白块也就变成了真实中文地名。


        IP属地:北京6楼2026-05-16 19:46
        回复
          以上全部是gpt5.5自己搞出来的,满打满算耗了我plus会员的三个周额度,按每个月80元土耳其区的会员价格,上述工作共耗费60元。但是让我这个程序设计60分的学渣一句代码没写,就解决了我从22年开始一直心心念念的mac下的汉化问题。我只能说,ai替代的海啸已经在地平线可以见到,但大部分人还认为这只是光线的错觉,直到真的被海啸淹没。


          IP属地:北京来自iPhone客户端7楼2026-05-16 19:52
          回复
            加油


            IP属地:广东8楼2026-05-16 22:12
            回复
              第一个版本的补丁已出,楼主就一台电脑没法大规模测试,欢迎大家下载尝试,报错及时反馈我才能知道问题来改。安装方式非常简单,就是在确保启用 52 汉化及汉化拓展的情况下,把启动器拖到 eu4 文件夹里双击打开即可。eu4 文件夹可以: steam库-eu4-设置-管理-浏览本地文件 打开。具体方法可以看网盘里的使用说明


              IP属地:北京9楼2026-05-17 00:12
              收起回复


                IP属地:天津来自Android客户端10楼2026-05-17 02:13
                回复
                  2026-06-07 13:34:17
                  广告
                  不感兴趣
                  开通SVIP免广告
                  dd


                  IP属地:海南来自Android客户端11楼2026-05-17 09:40
                  回复
                    牛批


                    IP属地:江苏来自Android客户端12楼2026-05-17 14:46
                    回复
                      可以用,但应该把app里的macos里的文件转化为可执行文件,用命令chmod+x 文件路径可以解决


                      IP属地:重庆14楼2026-05-18 18:50
                      收起回复
                        大佬反映的问题,我让 ai 解答一下


                        IP属地:北京15楼2026-05-18 23:02
                        回复