目录

前言

最近汉化了一个switch平台独占的文字冒险游戏。游戏由unity2022.3.18版本引擎驱动,使用TextMeshPro制作字体。过程比较坎坷,用时三天,记录一下过程,供其他想参与汉化的人参考。游戏比较有争议,因此汉化不放出,名称也不公布,仅做技术研究。

数据提取

从XCI文件中提取游戏原文件

游戏拿到手是xci格式的文件,需要先解包。解包用到的工具是hactool。我在GBATEMP论坛上找到了玩家开发的脚本,比较方便。

https://gbatemp.net/attachments/nca-nsp-xci_to_layeredfs_v1-6-5-zip.358656/

首先打开NCA-NSP-XCI_TO_LayeredFS.bat,选择4,1,再将XCI文件拖进去

图片1

这一步解包需要switch的prod.keys密钥,根据游戏所需系统版本的不同,密钥也不同,密钥可以在这里找到。下载的prod.keys需要放到C:\Users\user\ .switch文件夹内。

https://prodkeys.net/yuzu-prod-keys-v9/

密钥这玩意比较玄学,基本上靠蒙。我这里使用18.0.1版本的密钥成功解包。

之后会生成一个Extracted_XCI文件夹,里面是一些nca格式的文件。我们继续。打开NCA-NSP-XCI_TO_LayeredFS.bat,选择1,1,2,再将整个Extracted_XCI文件夹拖进去。

图片2

生成了Extracted_NCA文件夹,里面就是我们要的游戏原文件。

数据分析

打开文件夹,看到是非常典型的unity引擎打包的文件格式。

图片3

做文字冒险游戏汉化主要需要找到两个数据,一是游戏文本,二是字库。我这里很幸运,游戏文本没有被打包进去,在StreamingAssets\Script目录下有.h格式的脚本文件,直接修改文本就可以了。改了以后,进游戏,发现字库不够,中文全部显示方框。继续找字库修改。

使用UABE打开assets,提示版本不支持,游戏使用的unity引擎版本为2022.3.18f1,UABE仅支持3.4+/4/5/2017-2021.3版本。

图片4

UABE在2022年初已经停止维护了,查到有基于UABE的改进版本,UABEA目前还在维护。另外AssetRipper也可以打开该版本的assets文件进行提取。

UABEA主要用作修改游戏文件,并非用于提取,而且只能打开单文件。我们先使用AssetRipper,打开一整个文件夹,进行分析。

文件很多,慢慢找吧,关键词是font。最终在resources.assets这个资产包里面找到了相关文件,分析发现游戏使用TextMeshPro做字库。

图片5

TextMeshPro我也是第一次接触,TextMeshPro生成的字库有三样东西组成,

Material材质,主要是告诉引擎如何去渲染这个字体

Texture2D纹理,主要用于存储图像数据。在TextMeshPro中,字体通常会被打包到一个纹理图集(Atlas)中,而这个图集就是一个Texture2D对象。

MonoBehaviour,存储了字的Unicode和纹理的映射方式。

图片6

我们的思路是,制作一个自己的中文字体的TextMeshPro字库,用其中的Texture2D和MonoBehaviour替换它日文字体的字库,Material放着不管没关系,影响不大。

制作字库

首先我们打开unity hub,新建一个和游戏使用的引擎版本一样(尽量接近)的项目。我这里是2022.3.18版本。

我在我的汉化中想使用思源黑体。我们可以使用菜单栏中Windows-TextMeshPro-FontAssetCreator进行生成。这里我给出两种方法,一种是使用翠鸟社区用户InoryS制作完成的TMP字体,非常完善,不会缺字。

https://bbs.pha.pub/threads/130/

还有一种方法是自己生成,有一个需要注意的就是需要填写Custom Character List。我第一次是自己生成的,使用了网上给出的常用7000汉字,发现缺字,可能需要统计游戏文本中一共用到了哪些汉字,然后统一黏贴进去生成。推荐还是使用InoryS生成的TMP字体。

当然这里也给出自己制作的教程。

图片7

Source Font File,将自己的字体的tff或otf文件导入,拖过去。

Padding,文字像素大小,根据需要填写。

Atlas Resolution,生成的纹理的图片大小,尽量大。

Character Set,这里可以选填Unicode码的范围,也可以填需要的字符的列表,推荐是把游戏文本中需要用到的字全部放进去,Unicode码范围不好搞。

其他不用改,直接生成。

之后会生成一个asset文件,我们需要将里面的资产提取出来。

使用InoryS生成的TMP字体的,可以直接把下载下来的asset文件导入到项目里。我们在项目Assets目录下新建一个Font文件夹,把asset文件放进去。
点击asset文件,在unity右侧的inspecter的最底部,看到有Asset Lables,我们需要先给它随便打个标签,之后使用C#脚本打包。

图片8

在项目Assets目录下新建一个Editor文件夹,新建一个C#脚本,命名为BuildAssetBundlesScript.cs。

using UnityEditor;
using UnityEngine;
using System.IO;

public class BuildAssetBundlesScript
{
    [MenuItem("Tools/Build AssetBundles")]
    public static void BuildAllAssetBundles()
    {
        // 输出目录(建议不要放在 Assets 文件夹内)
        string assetBundleDirectory = "AssetBundles";
        if (!Directory.Exists(assetBundleDirectory))
        {
            Directory.CreateDirectory(assetBundleDirectory);
        }

        // 打包所有标记了 AssetBundle 标签的资源
        AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(
            assetBundleDirectory,
            BuildAssetBundleOptions.None,
            BuildTarget.StandaloneWindows64
        );

        if (manifest == null)
        {
            Debug.LogError("打包失败,请检查资源是否设置了 AssetBundle 标签!");
        }
        else
        {
            Debug.Log("AssetBundles 打包完成,输出目录:" + assetBundleDirectory);
        }
    }
}



脚本会在菜单栏中新建一个Tools-Build AssetBundles,并且输出到项目根目录的AssetBundles文件夹内。

点击Tools-Build AssetBundles

打开项目根目录(默认在C:\Users\username\My project\)找到AssetBundles文件夹,看到刚才打包的文件。我们使用uabea打开。

打开后截图记录一下File ID和 Path ID,一会儿有用。

首先提取Texture2D,使用plugins里面的Export texture,直接提取。导出后应该是一个png文件。

然后是提取MonoBehavior。这里有两种,一种是点击Export Dump,可以导出有可读性的json文件;另一种是点击Export Raw,导出原始dat文件,基本无可读性。

json文件能有可读性是因为uabea分析了这个资产的type tree。这里我们两种文件都要导出,一会儿都有用。

图片9

替换字库

替换MonoBehavior

下一步需要做的是,将MonoBehavior里面指向的资产的path id全都改成和游戏一样的。例如需要使用哪个材质进行渲染,使用哪个纹理,不改的话引擎就不知道。

打开提取的json文件,搜索Path ID,发现有很多。我们需要将其全部改为和游戏的那份一样的。

图片10

于是打开uabea,同样找到游戏字体的MonoBehavior,Export Dump,发现啥也没,只有一个头文件。

图片11

如果你汉化的游戏的使用的unity引擎版本比较低的话,或者你运气比较好,游戏没有使用IL2CPP之类的加密方式进行打包的话,大概率是能dump出json文件的。我这里运气比较差,啥也没。

于是我使用十六进制编辑器,例如010 editor,导出游戏原始的dat文件查看。反复看了很久,只有开头是稍微有点可读性的,经过对比得出,分别是m_Name,materialHashCode,m_SourceFontFileGUID,m_FamilyName。

其中m_FamilyName是字体原本的名称,可以看到我汉化的这款游戏使用了a-otf-ud-reimin-pr6n,UD黎ミン体。我们将这些字段全部替换。

图片12

那么还剩下很多pathid和fileid,如何找到对应的呢?我们搜索自己的json文件里面的pathid后发现,非常长非常奇怪,于是对比之前uabea中看到的pathid,发现分别是这个包里面的Material,Texture2D,MonoBehaviour的id。

那么就非常简单了,查看游戏的包,根据字体的名称,找对应的Material,Texture2D,MonoBehaviour的id,进行替换。

之后我们需要将修改好的文件替换过去,直接点import dump不行,无法生效,只有头部一小段会替换,剩余的部分无法替换。这里我找了一个办法,换成txt文件dump出来,修改完成以后,点击Edit Data,将修改完的txt文件全部复制过去,保存。成功生效。

图片13

当然,对于一般的游戏,修改json文件,使用import替换就行了。

替换Texture2D

这一步非常简单,使用plugins里面的Edit texture进行修改就可以了。load刚才导出的png纹理,save替换。

图片14

汉化文本

使用GalTransl进行汉化。相关的教程文档已经很详细了,不细说了。

打包

之后需要打包游戏,用到的工具是HacPack。

图片15

首先先打包生成nca文件。

Output Directory,输出文件夹,随便填。

Keyset File,prod.key文件,之前用哪个prod.key解包的,现在就用哪个打包。

Title ID,用yuzu模拟器可以看。

ExeFS Directory,填Extracted_NCA文件夹下修改好的ExeFS文件夹位置。

RomFS Directory,填Extracted_NCA文件夹下修改好的RomFS文件夹位置。

点击Build NCA。

等待生成。打包结束后,我们在output文件夹得到了最新的nca文件。

将最新的nca重命名为解包时最大的nca文件的名字,然后覆盖Extracted_NSP下的同名文件。

打包工具切换到NSP标签,点击Browse,选择Extracted_NSP文件夹,然后点击右下的Build NSP。

等打包结束后,output文件夹下就会出现我们自己整合的游戏文件了。