You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Python将JSON文件转换回Wasteland 3适用的.bundle资源包

废土3(Wasteland 3)语言包.bundle文件处理说明

现有解包代码功能

以下代码可实现将未压缩的Unity .bundle格式语言文件解析导出为JSON格式,但不包含JSON反向打包回.bundle格式的逻辑,目前可正常读取.bundle内容,但无法将修改后的JSON重新打包为游戏可识别的.bundle文件。

JSON反向打包.bundle实现方案

反向打包本质是将解包流程逆序实现,核心操作逻辑如下:

  • 保留原始未压缩.bundle文件的固定头部元数据,包括UnityFS魔数、版本号、节点信息、区块偏移量等无需修改的字段,不需要重新生成
  • 读取修改后的JSON文件,按解包时的字段顺序逐一序列化回二进制流:
    1. 依次写入m_GameObject的两个int32字段、4字节占位符、m_Enabled字段、m_Script的两个字段
    2. 写入m_Name的长度、UTF-8编码的字符串内容,随后补充对齐字节到4字节边界
    3. 遍历stringTables数组,依次写入每个表的Filename长度、内容、对齐字节,接着写入entryID数组的长度和所有int32格式的entryID,再写入femaleTexts数组的长度、每个文本的长度+内容+对齐字节,最后写入defaultTexts数组的长度、每个文本的长度+内容+对齐字节
  • 用新生成的二进制内容替换原始.bundle解析出的对应语言数据区块,重新计算各区块的大小、偏移量并写入对应字段
  • 按原始结构拼接所有区块后导出,即得到可被游戏识别的.bundle文件

注意事项

  • 所有操作前必须先将原始.bundle文件用UABE解压为无压缩状态,打包完成后也可通过UABE重新压缩
  • 数字序列化时的字节序要和读取时严格对应:小端(<)、大端(>)不能混淆
  • 所有变长字段写入后必须补0对齐到4字节边界,否则会导致游戏读取崩溃

现有.bundle转JSON解包代码

import struct, sys
def calculate_padding_len(ilen):
    res = (ilen % 4)
    if res == 0:
        return 0
    else:
        res = 4 - res
        return res


def int32(reader, endian=">"):
    return struct.unpack(endian + "i", reader.read(4))[0]

def int64(reader, endian=">"):
    return struct.unpack(endian + "q", reader.read(8))[0]
class Node():
    pass
def ReadtoNull(reader):
    savepos = reader.tell()
    stre = reader.read().split(b"\x00")[0]
    reader.seek(savepos+len(stre) + 1)
    return stre
def export(inf):
    import os
    #os.makedirs(out, exist_ok=True)
    inf = open(inf, "rb")
    magic = ReadtoNull(inf)
    if magic != b"UnityFS":
        return 0

    version = int32(inf)
    ReadtoNull(inf)
    ReadtoNull(inf)
    size = int64(inf)
    compressed = int32(inf)
    inf.read(4)
    flags = int32(inf)
    if flags != 64:
        #raise ValueError(f"This is not decompressed\nDecompress file with UABE first")
        DD = flags & 0x3F
        match DD:
            case 1:
                raise ValueError("LZMA Compression isn't supported, please decompress file with UABE first.")
            case 2:
                raise ValueError("LZ4 Compression isn't supported, please decompress file with UABE first.")
            case 3:
                raise ValueError("LZ4HC Compression isn't supported, please decompress file with UABE first.")
            case default:
                raise ValueError("Unknown Compression")
    dataHash = inf.read(16)
    unkcount = int32(inf)
    topl = 0
    for i in range(unkcount):
        a = int32(inf, ">")
        topl += a
        inf.read(6)
    nodes_count = int32(inf)
    Nodes = []
    import io
    for i in range(nodes_count):
        newNode = Node()
        newNode.offset = int64(inf)
        newNode.size = int64(inf)
        newNode.flags = int32(inf)
        newNode.path = ReadtoNull(inf)
        Nodes.append(newNode)
    allCab = io.BytesIO(inf.read(topl))
    MyNode = Nodes[0]
    allCab.seek(MyNode.offset)
    MyCab = io.BytesIO(allCab.read(MyNode.size))
    MyCab.seek(12)
    dataStart = int32(MyCab)
    MyCab.seek(36)
    fileCount = int32(MyCab, "<")
    MyCab.seek(dataStart - 84)
    fileSizes = [int32(MyCab, "<"), int32(MyCab, "<")]
    MyCab.read(16)
    fileSizes.append(int32(MyCab, "<"))
    MyCab.seek(dataStart)
    memories = []
    for i in fileSizes:
        memories.append(MyCab.read(i))
    fileNameListbutMemory = io.BytesIO(memories[-1])
    
    fileList = []
    #fileNameListbutMemory.read(4)
    for i in range(fileCount):
        size = int32(fileNameListbutMemory, "<")
        name = fileNameListbutMemory.read(size)
        fileNameListbutMemory.read(calculate_padding_len(size))
        fileList.append(name)
        
        savepos = fileNameListbutMemory.tell()
        spl = fileNameListbutMemory.read().split(b"\x01\x00\x00\x00")[0]
        fileNameListbutMemory.seek(savepos + len(spl) + 4)
    for name,data in zip(fileList, memories):
        
        if b"/" in name:
            basename = b".".join(name[::-1].split(b".")[1:]).split(b"/")[0][::-1] + b".json"
            data = io.BytesIO(data)
            out = {}
            out["m_GameObject"] = {}
            out["m_GameObject"]["m_FileID"] = int32(data, "<")
            out["m_GameObject"]["m_PathID"] = int32(data, "<")
            data.read(4)
            out["m_Enabled"] = int32(data, "<")
            out["m_Script"] = {}
            out["m_Script"]["m_FileID"] = int32(data, "<")
            out["m_Script"]["m_PathID"] = int64(data, "<")
            namesize = int32(data, "<")
            out["m_Name"] = data.read(namesize).decode()
            data.read(calculate_padding_len(namesize))
            string_table_length = int32(data, "<")
            out["stringTables"] = []
            for i in range(string_table_length):
                Filename_length = int32(data, "<")
                newdict = {}
                newdict["Filename"] = data.read(Filename_length).decode()
                data.read(calculate_padding_len(Filename_length))
                lenentryid = int32(data, "<")
                newdict["entryIDs"] = []
                for a in range(lenentryid):
                    newdict["entryIDs"].append(int32(data, "<"))
                lengfemaleTexts = int32(data, "<")
                newdict["femaleTexts"] = []
                for a in range(lengfemaleTexts):
                    size = int32(data, "<")
                    newdict["femaleTexts"].append(data.read(size).decode())
                    data.read(calculate_padding_len(size))
                newdict["defaultTexts"] = []
                lendef = int32(data, "<")
                for a in range(lendef):
                    size = int32(data, "<")
                    newdict["defaultTexts"].append(data.read(size).decode());data.read(calculate_padding_len(size))
                out["stringTables"].append(newdict)
                import json
            with open(basename, "w", encoding="utf8") as fp:
                json.dump(out,fp, indent=2)
            
    
    
    
    
if not len(sys.argv) == 1:
    export(sys.argv[1])
else:
    print("Usage:")
    print("\tbundle.py %s" % "[BUNDLE]")

内容的提问来源于stack exchange,提问作者Syfity

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 08:45:04