红警2 csf文件解析 简体化

    之前下了个红警尤里复仇是繁体的,看着很难受。所以趁着放假把它简体化一下。

    语言文件存储在langmd.mix->ra2md.csf文件中,使用xcc mixer可以把它提取出来,放到游戏文件目录即可。

    csf文件格式:CSF File Format on ModEnc, the Command & Conquer Modding Encyclopedia

    这个文件可以用 红警文本csf编辑器去一条一条的编辑,但是效率有点低。

     既然已经知道csf文件格式,直接用Python将其中的文字提取出来,然后保存到Word中,然后使用Word的繁体转简体,一键转换。最后再把简体字按csf文件格式重新保存就可以了。

 

 以下是转换需要的Python代码:   

# 解析红警2的csf文件
import os
import struct

def read_dword(f):
    return struct.unpack('<I', f.read(4))[0]


def write_dword(f, num):
    return f.write(struct.pack('<I', num))


class CsfHeader(object):
    # 0x0	char[4]  	" FSC"
    identifier = b' FSC'
    langMap = ['US', 'UK', 'German', 'French', 'Spanish', 'Italian',
               'Japanese', 'Jabberwockie', 'Korean', 'Chinese', 'Unknown']

    def __init__(self) -> None:
        # 0x4	DWORD       CSF Version
        self.version = 0
        # 0x8	DWORD	    NumLabels
        self.NumLabels = 0
        # 0xC	DWORD       NumStrings
        self.NumStrings = 0
        # 0x10	DWORD       (unused)
        # 0x14	DWORD       Language
        self.Language = 0

    def __str__(self):
        s = 'NumLabels= %d, NumStrings= %d, Language= %s' % (
            self.NumLabels, self.NumStrings, CsfHeader.langMap[self.Language])
        return s

    def save(self, f):
        f.write(CsfHeader.identifier)
        write_dword(f, self.version)
        write_dword(f, self.NumLabels)
        write_dword(f, self.NumStrings)
        # (unused)
        write_dword(f, 0)
        write_dword(f, self.Language)
        

class LabelHeader(object):
    # 0x0	char[4]     " LBL"
    identifier = b' LBL'

    def __init__(self) -> None:
        # 0x4	DWORD       Number of string pairs
        self.Number_of_string_pairs = 0
        # 0x8	DWORD       LabelNameLength
        self.LabelNameLength = 0
        # 0xC	char[LabelNameLength]   LabelName
        self.LabelName = ''

    def save(self, f):
        f.write(LabelHeader.identifier)
        write_dword(f, self.Number_of_string_pairs)
        write_dword(f, self.LabelNameLength)
        f.write(self.LabelName)

class LabelValue(object):
    # 0x0	char[4]         " RTS" or "WRTS"
    identifiers = [b' RTS', b'WRTS']

    def __init__(self) -> None:
        # 0x4	DWORD       ValueLength
        self.ValueLength = 0
        # 0x8	byte[ValueLength*2]                         Value
        self.Value = ''
        # 0x8+ValueLength*2	DWORD                           ExtraValueLength
        self.ExtraValueLength = 0
        # 0x8+ValueLength*2+0x4	char[ExtraValueLength]      ExtraValue
        self.ExtraValue = ''
        self.text = ''

    def decode(self):
        self.Value = bytes([~x & 0xff for x in self.Value])
        self.text = self.Value.decode('utf16')
        return self.text

    def encode(self):
        self.Value = bytes([~x & 0xff for x in self.Value])
        return self.Value

    def replace(self, text):
        self.text = text
        # 注意,这里需要指明编码大小端, 只写 'utf16' 会多出两个字节指示编码的大小端
        self.Value = text.encode('utf-16-le')
        self.ValueLength = len(text)

    def save(self, f):
        if self.ExtraValueLength == 0:
            f.write(LabelValue.identifiers[0])
        else:
            f.write(LabelValue.identifiers[1])
        write_dword(f, self.ValueLength)
        f.write(self.encode())
        if self.ExtraValueLength != 0:
            write_dword(f, self.ExtraValueLength)
            f.write(self.ExtraValue)


class Label(object):
    def __init__(self) -> None:
        self.header = LabelHeader()
        self.values = []

    def save(self, f):
        self.header.save(f)
        for v in self.values:
            v.save(f)


# Header


def parse_csf_header(f):
    f.seek(0, 0)
    if f.read(4) != CsfHeader.identifier:
        print("Parse Csf Header Failed!")
        return False
    header = CsfHeader()
    header.version = read_dword(f)
    header.NumLabels = read_dword(f)
    header.NumStrings = read_dword(f)
    f.read(4)
    header.Language = read_dword(f)

    return header


def parse_label_header(f):
    if f.read(4) != LabelHeader.identifier:
        print("Parse Label Header Failed!")
        return False
    header = LabelHeader()
    header.Number_of_string_pairs = read_dword(f)
    header.LabelNameLength = read_dword(f)
    header.LabelName = f.read(header.LabelNameLength)

    return header


def parse_label_value(f):
    t = f.read(4)
    if t not in LabelValue.identifiers:
        print("Parse Value Failed!")
        return False
    value = LabelValue()
    value.ValueLength = read_dword(f)
    value.Value = f.read(value.ValueLength * 2)
    if t == LabelValue.identifiers[1]:
        value.ExtraValueLength = read_dword(f)
        value.ExtraValue = f.read(value.ExtraValueLength)

    value.decode()

    return value

# Label


def parse_label(f):
    header = parse_label_header(f)
    if not header:
        print("Parse Label Failed!")
        return False

    label = Label()
    label.header = header
    for i in range(header.Number_of_string_pairs):
        value = parse_label_value(f)
        if not value:
            return label
        label.values.append(value)

    return label


def parse_csf(filename):
    f = open(filename, 'rb')
    header = parse_csf_header(f)
    if not header:
        print("Parse Csf Failed!")
        return False
    labels = []
    for i in range(header.NumLabels):
        if i == 1270:
            print("break")
        label = parse_label(f)
        if not label:
            break
        labels.append(label)

    f.close()
    return (header, labels)


def dump_texts(filename: str, labels: Label):
    f = open(filename, 'w', encoding='utf8')
    for l in labels:
        for v in l.values:
            s = '[%s]%s'%(l.header.LabelName.decode(), v.text)
            f.write(s + '\n')

    f.close()

    return True

def read_translate(filename:str):
    translations = {}
    name = ''
    value = ''
    with open(filename, 'r', encoding='utf8') as f:
        while True:
            line = f.readline()
            if line == '':
                break
            if line[0] != '[':
                translations[name] += '\n' + line[:-1]
                continue
            pos = line.index(']')
            name = line[1:pos]
            value = line[pos + 1:-1]
            translations[name] = value

    print("translations count: %d"%len(translations))

    return translations

def replace_translation(labels, translations):
    for l in labels:
        for v in l.values:
            name = l.header.LabelName.decode()
            new_text = translations[name]
            v.replace(new_text)

    return labels


def main():
    filename = r'G:\hongj2yuri\RA2YR\ra2md.csf'
    header, labels = parse_csf(filename)
    print(str(header))
    # 提取繁体字信息
    # df = os.path.splitext(filename)[0] + '.txt'
    # dump_texts(df, labels)
    
    # Word繁体到简体转换,然后新建一个txt文档,保存转换后的结果

    # 读取转换后的简体字信息
    translations = read_translate(r'G:\hongj2yuri\RA2YR\ra2md_sc.txt')
    # 转换所有Label中的繁体信息
    labels = replace_translation(labels, translations)
    # 保存新文件
    nf = os.path.splitext(filename)[0] + '_sc.csf'
    with open(nf, 'wb') as f:
        header.save(f)
        for l in labels:
            l.save(f)

    return True


main()

转换后的效果:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值