求助,关于圣城大亨游戏怎么提取(已解决,感谢d-miracle大佬和tien88大佬的分享,以及其它回复的朋友们)

好的,我研究一下 :face_with_monocle:

png和atlas都有明确的文件头和文件尾特征,用于和其他数据切割。skel我只看出来4.1.2前面9个字节就是文件开头,skel的文件结尾该怎么判断啊?

尾部很难判断. 我用的很粗糙的方法. 向后找到下一个文件头特征(PNG、JPG、或其他 skel header) 并限制skel大小不超过1M,取其位置为结束点. 只能说 用这个方法能分割出绝大多数的skel 但有少部分skel超出了这个条件. 所有才有30个左右的skel文件我也没能分割出来.

from pathlib import Path
from io import BufferedReader

def parse(r: BufferedReader, r2: BufferedReader, outPath: Path):
    count = int.from_bytes(r.read(4), 'little')
    for _ in range(count):
        nameLen = r.read(1)[0]
        name = bytes(i ^ nameLen for i in r.read(nameLen))
        start = int.from_bytes(r.read(4), 'little') - name[-6] - nameLen
        size = int.from_bytes(r.read(4), 'little') - name[-7] - nameLen
        new = outPath.joinpath(name.decode())
        new.parent.mkdir(parents=True, exist_ok=True)
        r2.seek(start)
        with open(new, 'wb') as w:
            w.write(r2.read(size))

def batch(datPath: str = '', outPath: str = '', subfolder: bool = False):
    path = Path(datPath) if datPath else Path.cwd()
    move = (Path(outPath) if outPath else path).joinpath('Extract')
    need = [i for i in (path.rglob('*.dic') if subfolder else path.glob('*.dic'))]
    for i in need:
        dat = i.parent.joinpath(f'{i.stem}.dat')
        if not dat.is_file():
            print(f'缺少dat文件 --- {dat.as_posix()}')
            continue
        with open(i, 'rb') as f, open(dat, 'rb') as f2:
            parse(f, f2, move)

if __name__ == '__main__':
    path = r'' # 游戏文件所在的路径
    outPath = r'' # 导出文件的路径
    subfolder = False # 是否查找字文件夹内的`.dic`文件
    batch(path, outPath, subfolder)

大概就是这样
起始位置计算是这样的 起始位置 - 名字字节的结尾第6个字节 - 名字长度
大小也差不多, 不过是减去名字字节的结尾第7个字节

大佬,这是用PY批处理是吗?

太强了! 不愧是大佬. 这才是优雅的提取方式.

对, 你只需要把dat和dic文件放在一个文件夹里, 然后path设置为那个文件夹的路径, 运行脚本就行了

能讲讲思路吗?

这孰能生巧…我经常处理二进制文件, 所以猜测这种简单的结构比较轻松, 主要对比前几个文件的起始位置和大小, 然后猜测运算结果, 这一步花了点时间

辛苦大佬了,对了,这些文件里有语音吗?

我没检查, 测了下随机导出了几个文件, 确定拆分没问题我就把文件都删了