Balrum 汉化指引

来源:互联网 发布:九九算法算生男女公式 编辑:程序博客网 时间:2024/06/07 06:34

Balrum 汉化指引


准备工具

- TexturePacker 图片字库打包工具 [官网](http://texturepacker.com/)- Java 编程工具 (我用的Intellij IDEA ,有免费社区版,自带代码反编译)- 自己喜欢的代码工具,一般游戏的字库都是不能直接用的,所以掌握一门语言自己写工具还是必要的。- BMFont (强大的图片字库工具,一般都是用这个工具生成基本图片后,自己再用工具生成不同游戏的图片字库描述文件。)[官网](http://www.angelcode.com/products/bmfont/)- 一些汉化知识...后面有啥再补

预汉化步骤

1. 找到游戏文本

首先这个第一步其实就是很麻烦的过程,简单的游戏,打开文件夹翻翻目录,文件,然后用全文搜索工具搜一下文件夹里面的内容就可以找到文本。比如这个balrum,游戏的文本随便翻一下就找到了。比较复杂的直接就资源加密了,直接搜不出来,只能靠反编译看代码实现了。

2.找到游戏字库

游戏字库其实也分两类,一是TTF之类的字库,二是图片字库。其实对于程序内部来说这两种字库最终都是会以贴图形式显示出来。一般TTF之类的字库因为通用程度高,但是普遍没啥特效。那种一看就很花里胡哨的文字基本上都是PS出来的图片字库。另外找到文本以后,直接修改文本显示内容测试一下就知道是不是图片字库。反正汉化的最终目的是显示中文,可以显示的话基本上就可以找翻译了。

3.分析字库技术

如果是TTF或者系统字库什么的,我们基本上可以不用管,图片字库什么的就比较复杂了。不过图片字库有很多通性。基本上都有一张图片,一个字库描述文件。图片就不解释了,所谓字库描述文件就是一个文件记录了图片上不同的字对应的位置、大小、偏移等等信息。图片一般英文字库我们肯定不能直接拿来用,基本都是需要自己生成。我这里用的就是BMFont这个工具。至于字库描述文件,这里面道道就多了,简单一点的就是拿通用字库工具生成的,比如Balrum。复杂的就是各种自定义格式,甚至二进制格式,用文本编辑器打开就是一堆乱码。(PS:所以balrum这个游戏汉化应该没啥难度,就是太麻烦了,贴吧上弄了这么久,也没人接手,可能是人气太低吧。Java其实也不算太难,而且这个游戏还没有混淆加密之类的,把jar文件解压往IDEA里面一拉 双击class文件就可以看到代码了。不过如果对于汉化有兴趣的同学不妨试着自己分析一下这个游戏,差不多预汉化工作就介绍到这里。)

4.根据找到的资源分析整理

因为这个游戏我已经做过一次汉化,这么多的文件是做什么的,要改什么地方其实是最关键的地方。主要手段有几种:

  • 静态资源分析:假如字库就是一个ttf文件,所有的文本都是可以修改的明文。这些资源都是静态文件,而且有一定的程序基础就可以通过简单替换文件,修改文本实现汉化。如果经验再丰富点,可以简单通过一些资源的开始几个字节得知这是一个图片文件,压缩文件或者是其他字库文件等等。
  • 代码反编译分析:对于很多资源加密的游戏,只能进行反编译分析了。不过这个也有技术难度高低,直接阅读汇编还是不太现实的。好在现在有很多工具可以直接反编译出部分代码。虽然不一定准确,但是还是有很大的参考价值的。目前比较容易反编译的一般JAVA/C#/flash之类的游戏。可以简单搜索一下相应的反编译工具就可以阅读部分源码。从而推测出字库格式。当然了,大作一般都是各种加压加密的,这一块是个巨坑,量力而为吧。
  • 瞎猜:以上都不会没经验就只能这样了,说不定有瞎猫碰到死耗子的一天。

顺便说下Balrum,我是直接用IDEA打开class文件就可以查看Java源码了。而且Balrum目录下有个debug.txt记录游戏出错的原因。改完一点就通过这个报错 ,打开源码参考哪里出了问题,过程有点长且无聊。我这边记录汉化需要修改的文件跟作用。

汉化正式开始!


修改资源列表

  • Balrum\files\textures\gui :其实textures文件夹下的所有文件都是png图片,只要加上.png后缀就可以直接打开查看了,打开后就会发现这个大图是很多小图拼接在一起的,小图具体说明就是下面的gui.xml文件。
  • Balrum\files\xml\gui.xml:两个文件是直接用TexturePacker打包来的。设置截图跟安装包都在网盘压缩包里。不过找到工具是打包用的,我们改完图片之后,可以用工具打包回去,但是修改图片必须自己解包了。解包部分说明见下面
  • Balrum\files\xml\fonts.xml:字库的描述文件,其实就是BMFont生成的xml文件格式稍微修改了一点点。至于x,y,width等等具体可以参考FreeType字形约定:https://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html
  • Balrum\files\xml\kerning.xml:同样是描述文字用的。英文字体不像中文,每个字符宽度不是一样的,为了让显示效果更美观的字距文件。具体同样参考FreeType字形约定:https://www.freetype.org/freetype2/docs/glyphs/glyphs-4.html
  • Balrum.jar\guisystem\font\Fonts_DATA.class :JAVA代码原生支持Unicode,理论上原生支持中文显示。测试过程中发现,游戏字库载入代码是写死的,无法载入超过unicode编码8225以上文字。而中文编码基本都在19000-40000之间,所以必须修改代码才能载入中文。(这里其实载入的就是上面几个文件,其一个TTF里面其实就包含了上面的所有信息。不过效果单一,显示比较单调。所以很多游戏还是用图片字库)
    注:大部分的工具,代码我都整理放在了一起。 链接:https://pan.baidu.com/s/1i5bV78L 密码:q0io

解包图片字库

我这里直接上代码,我平时写工具还是习惯用python,安装python什么就不说了,我这里用了一个Pillow库处理图片切割
安装完python执行

python -m pip install pillow

就可以了,以下为python代码

# -*- coding: utf-8 -*-import osimport xml.etree.ElementTree as ETfrom PIL import Imagedef unpackpng(xmlfile):    tree = ET.ElementTree(file=xmlfile)    xmlroot = tree.getroot()    pngfile = xmlroot.attrib['imagePath']    pngpath = pngfile.replace(".", "_")    if not os.path.exists(pngpath):        os.mkdir(pngpath)    try:        with Image.open(pngfile) as im:            print(pngfile, im.format, "%dx%d" % im.size, im.mode)            for child in xmlroot:                attr = child.attrib                name = attr['n'] + '.png'                x, y, w, h = int(attr['x']), int(attr['y']), int(attr['w']), int(attr['h'])                ox, oy, ow, oh = int(attr.get('oX', 0)), int(attr.get('oY', 0)), int(attr.get('oW', w)), int(attr.get('oH', h))                offsetbox = (ox, oy, ox + w, oy + h)                box = (x, y, x + w, y + h)                sprite = im.crop(box)                subim = Image.new(im.mode, (ow, oh), (0, 0, 0, 0))                subim.paste(sprite, offsetbox)                subim.save(os.path.join(pngpath, name), format=im.format)    except IOError:        passif __name__ == '__main__':    unpackpng("gui.xml")

压缩包里面在main.py里面,使用方法是把gui改名成gui.png,还有gui.xml放在main.py一起,直接执行unpackpng("gui.xml") 就可以了。小图全都会解压到gui_png里面。不过我工具包里面已经有一份newgui 解包好的图片资源。

检查文本数据

因为之前汉化者给我的文本是手工修改xml文件的,Balrum这个项目一共有375个文件。这么多文件是很难保证不出差错的。我这里写了一个简单的xml文本校验工具。代码在checktext.py里面。用法是把所有文本文件复制进text目录,执行文件就可以。不过事实证明,还是有不少错误。所以窝顺便这里安利一波Passolo工具。这个工具可以设置一定的文本采样规则,然后一次性把需要翻译的文本采集进来。采集好后保存的lpu文件可以发送给别的翻译,翻译完成后再通过规则生成一样的文件。可靠性比人工修改文本还是高的。并且对于翻译前后文,校对都很有帮助。

说到这里补充一点文本编码信息,通常情况来说,现在的计算机内存中文本信息基本上都是UNICODE类型,但是这些文本信息要保存成文件的时候,就有个文件编码格式的问题。目前主流有以下UTF-8/UTF-16,GB2312/GBK/BIG5等等,其中UTF又有带BOM跟不带BOM区别。不过我们不用了解太多,理解成一个压缩文件保存成rar跟zip的区别。只要我们修改文本的时候注意一下,看看游戏能不能识别,哪种格式都是可以的(主要指UTF-8带BOM跟不带BOM)。我在工具包里面附带了一个批量转换编码的工具,我下面的文本处理都是统一转换成UTF-8处理的

生成字库文件

采集文本字符集

简单来说就是游戏中所有要显示的文字。
还是直接上代码

import osimport codecsimport shutilimport stringimport xml.etree.ElementTree as ETdef gettext():    oldstring = u"""  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƏƒƠơƯưƷǴǵǺǻǼǽǾǿȘșȚțəʒʹʺˆˇˉ˘˙˚˛˜˝̣̦̀́̃̉̒ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѢѣѪѫѲѳѴѵҐґҒғҖҗҘҙҚқҜҝҠҡҢңҪҫҮүҰұҲҳҶҷҸҹҺһӁӂӐӑӖӘәӢӣӨөӮӯӲӳẀẁẂẃẄẅẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹ‐‑‒–—―‖‗‘’‚‛“”„†‡•‥…‰′″‹›‼‽‾⁄"""    fonttable = set(unicode(string.printable + oldstring))    count = 0    for record in os.walk("text/"):        subpath, subdir, subfile = record        for xmlfile in subfile:            if xmlfile.endswith(".xml"):                xfilepath = os.path.join(subpath, xmlfile)                count += 1                with codecs.open(xfilepath, encoding='utf-8') as xfile:                    fonttable.update(xfile.read())    fontnum = len(fonttable)    print " %d xml files read, %d fonts." % (count, fontnum)    with codecs.open('BMFont/alltext.txt', 'w', encoding='utf_8_sig') as alltext:        alltext.write("".join(fonttable))

这段代码就是从前面的text文件夹遍历所有xml文件读取后生成一个使用所有汉字的字表。
前面那串乱七八糟的字符是游戏的原本字库的字符。
执行完成后就可以看见BMFont目录下有个alltext.txt文件,里面就是文本所需的全部文字

调用BMFont生成图片字库

import osimport codecsimport shutilimport stringdef gen_picfonts():    bmfpath = os.path.abspath(os.path.join(os.path.curdir, 'BMFont'))    bmfexe = os.path.abspath(os.path.join(bmfpath, "bmfont.com"))    cmd = "{bmfexe} -c {bmfpath}\\fontconfig.bmfc -t {bmfpath}\\alltext.txt -o font.fnt".format(        bmfpath=bmfpath, bmfexe=bmfexe)    os.system(cmd)    if os.path.exists("font_0.png"):        if os.path.exists("font16.png"):            os.remove("font16.png")        os.rename("font_0.png", "font16.png")

其中fontconfig.bmfc是生成字库的配置文件,可以根据情况修改
我这段代码其实只生成了一个字库,之所以这样做是因为前面生成文本后统计了一下已经超过3000个汉字了。而gui图片大小只有2048*2048。哪怕是最小的字库也只能勉勉强强够放进去而已。所以游戏代码加载的地方我还修改了所有字体都变成了最小字体。

生成字体所需xml文件

import osimport codecsimport shutilimport stringimport xml.etree.ElementTree as ETdef conv_xml():    fontfile = "font.fnt"    before = '<?xml version="1.0"?>\n<fonts>\n    <fonts_size_16>\n'    end = '    </fonts_size_16>\n</fonts>'    if os.path.exists(fontfile):        tree = ET.ElementTree(file=fontfile)        xmlroot = tree.getroot()        with codecs.open('fonts.xml', 'w', encoding='utf_8_sig') as fontxml:            fontxml.write(before)            for char in xmlroot.find("chars"):                charfmt = '        <char id="{id}" x="{x}" y="{y}" width="{width}" height="{height}" ' \                          'xoffset="{xoffset}" yoffset="{yoffset}" xadvance="{xadvance}" ' \                          'page="{page}" chnl="{chnl}" />\n'                fontxml.write(charfmt.format(**char.attrib))            fontxml.write(end)    if os.path.exists("fontsold.xml"):        os.remove("fontsold.xml")

仔细看下font16.fnt跟fonts.xml还是有差别的。所以我们为了省事又写了个生成xml文件的代码。
就是上面这段。

重新打包文件

执行完上面步骤后文件夹下面有一个font16.png文件,把这个文件复制进newgui 里面,删掉其他的font**.png文件(因为我们只做了一个字库)
打开TextPacker
按图片设置
设置
路径你们看着写,点击发布。
这个时候目录下应该已经有 gui,gui.xml,fonts.xml文件(kerning.xml忘记了)
覆盖对应的文件

修改主程序

这个也很简单,可以直接用rar或其他压缩软件打开Balrum.jar(打开不是解压),把我改好的class文件覆盖对应文件覆盖就好
这里放上源码

package guisystem.font;import balconytools.FilePathHandlerOWL;import gamedir.Globals;import guisystem.font.FastFonts;import java.io.File;import java.io.IOException;import java.io.PrintStream;import java.util.List;import org.jdom2.Attribute;import org.jdom2.Document;import org.jdom2.Element;import org.jdom2.JDOMException;import org.jdom2.input.SAXBuilder;public final class Fonts_DATA {    public static final FastFonts[] fontarraysmall = new FastFonts[65535];    public static final FastFonts[] fontarraymedium = fontarraysmall;    public static final FastFonts[] fontarraybig = fontarraysmall;    public static final FastFonts[] fontarrayextra = fontarraysmall;    public static final FastFonts[] fontarraymediumblack = fontarraysmall;    public static int font16X;    public static int font16Y;    public static int font20X;    public static int font20Y;    public static int font25X;    public static int font25Y;    public static int font32X;    public static int font32Y;    public static int font20NoShadowX;    public static int font20NoShadowY;    private Fonts_DATA() {    }    public static void build() {        int id;        Document document;        try {            int i;            List lista;            Element node;            document = Globals.sax_builder.build(FilePathHandlerOWL.owlNewFile((String) FilePathHandlerOWL.xmls, (String) "fonts.xml"));            List list = document.getRootElement().getChildren();            List children_list = ((Element) list.get(0)).getChildren();            Element kerdojel = (Element) children_list.get(63);            for (i = 0; i < children_list.size(); ++i) {                node = (Element) children_list.get(i);                lista = node.getAttributes();                id = ((Attribute) lista.get(0)).getIntValue();                Fonts_DATA.fontarraysmall[id] = new FastFonts(lista, 2, 2);            }        } catch (IOException io) {            System.err.println(io.getMessage());        } catch (JDOMException jdomex) {            System.err.println(jdomex.getMessage());        }        try {            List lista;            Element node;            int i;            document = Globals.sax_builder.build(FilePathHandlerOWL.owlNewFile((String) FilePathHandlerOWL.xmls, (String) "kerning.xml"));            List list = document.getRootElement().getChildren();            List children_list = ((Element) list.get(0)).getChildren();            for (i = 0; i < children_list.size(); ++i) {                node = (Element) children_list.get(i);                lista = node.getAttributes();                id = ((Attribute) lista.get(0)).getIntValue();                fontarraysmall[id].add_kerning(lista);            }            return;        } catch (IOException io) {            System.err.println(io.getMessage());            return;        } catch (JDOMException jdomex) {            System.err.println(jdomex.getMessage());        }    }}

需要注意的是

Fonts_DATA.fontarraysmall[id] = new FastFonts(lista, 2, 2);

这一句,这个2,2其实是字体图片文件font16.png在gui.xml中x,y的位置。原程序把这些位置也是写死了。
我测试过几次 用TextPacker不出意外基本上这个坐标是不会变的。变了的话就得重新生成class了。

最后一步

删除

Balrum\files\cache

里面所有内容。gui图片变更需要删掉缓存重新生成。不然会显示乱码。

到了这一步就应该大功告成。 可以看一下显示效果或者修改文本自己生成字库了。
当然字库样子还有点挫,可以通过调整fontconfig.bmfc尝试一下别的字体或者效果。

补充

1、后来测试字库的时候,发现图片字库太大会导致程序载入异常,虽然图片是1024x1024大小,但是测试差不多1024*850左右就无法载入了
2、文本换行处理,简单来说就是利用英文空格自动换行的特点,每个汉字后加一个空格(Notepad++,目录搜索替换 ([^\x00-\xff]) $1 注意后面$1后有个空格),但是这样游戏中文本看起来间距很大,这样可以通过修改图片字库,id=32(空格的unicode码)width height xoffset yoffset xadvance全部设置为0,这样空格就显示不出来了。那如果还要显示空格怎么办?用全角空格!
3、其他的想到再补充吧

感谢

感谢贴吧hastejackal0的文本翻译。太多一时热情汉化一点文本就放弃的,一个人坚持翻译完这么多文本不容易
链接:http://pan.baidu.com/s/1kVbm9rp 密码:hcds

0 0
原创粉丝点击