解析帝国时代游戏记录文件(.mgx)

2024-09-28 03:12:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我是一个过时的游戏帝国时代2(AoE)的球迷。我想用Python编写一个AoE游戏记录(.mgx文件)的解析器。在

我在GitHub上做了一些搜索,找到了一些关于这个的小项目,最有用的是提供some details of .mgx game record filesaoc-mgx-format。在

问题是:

根据参考文献,.mgx文件的结构如下:

| header_len(4byte int) | next_pos(4byte int) | header_data | ... ... |

mgx格式的十六进制数据的字节顺序是LIGHT endian。在

标题长度存储标题部分的数据长度(标题长度+下一篇文章+标题数据)

header_data存储我需要的有用信息,但它是用zlib压缩的

我尝试用zlib模块解压缩头_数据中的数据,如下所示:

import struct
import zlib

with open('test.mgx', "rb") as fp:
    # read the header_len bytes and covert it to a int reprents length of Header part
    header_len = struct.unpack("<i", fp.read(4))[0]

    # read next_pos (this is not important for me)
    next_pos = struct.unpack("<i", fp.read(4))[0]

    # then I can get data length of header_data part(compressed with zlib)
    header_data_len = header_len - 8

    compressed_data = fp.read(header_data_len)[::-1] # need to be reversed because byte order is little endian?

    try:
        zlib.decompress(compressed_data)
        print "can be decompressed!"
    except zlib.error as e:
        print e.message

但我在运行程序后得到了这个:

Error -3 while decompressing data: incorrect header check

可以在这里找到sample.mgx文件:https://github.com/stefan-kolb/aoc-mgx-format/tree/master/parser/recs


Tags: 文件of数据pos标题readdatalen
2条回答

您的第一个问题是不应该反转数据;只需去掉[::-1]。在

但是如果你这样做,你得到的不是错误-3,而是另一个错误-3,通常是关于一个未知的压缩方法。在

问题是这是一个无头的zlib数据,很像gzip使用的数据。理论上,这意味着有关压缩方法、窗口、start dict等的信息必须在文件中的其他地方提供(在gzip中,是通过gzip头中的信息提供的)。但在实践中,每个人都使用deflate with the max window size and no start dict,所以如果我在为游戏设计一个紧凑的格式,那时候每个字节都是计数的,我只需要硬编码。(在现代,这在RFC中被标准化为“压缩数据格式”,但90年代的大多数PC游戏并没有按照RFC设计……)

所以:

>>> uncompressed_data = zlib.decompress(compressed_data, -zlib.MAX_WBITS)
>>> uncompressed_data[:8] # version
b'VER 9.8\x00'
>>> uncompressed_data[8:12] # unknown_const
b'\xf6(<A'

所以,它不仅被解压,看起来像是一个版本,而且…我猜任何东西看起来都像一个未知常数,但它在规范中是相同的未知常数,所以我认为我们很好。在

正如^{}文档所解释的,MAX_WBITS是默认的/最常见的窗口大小(也是通常称为“zlib deflate”而不是“zlib”使用的唯一大小),传递负值意味着头被抑制;其他参数可以保留为默认值。在

另请参见this answerzlib文档中的Advanced Functions部分和RFC 1951。(感谢OP找到链接。)

老了,但下面是我所做的一个例子:

class GameRecordParser:

def __init__(self, filename):
    self.filename = filename
    f = open(filename, 'rb')

    # Get header size
    header_size = struct.unpack('<I', f.read(4))[0]
    sub = struct.unpack('<I', f.read(4))[0]
    if sub != 0 and sub < os.stat(filename).st_size:
        f.seek(4)
        self.header_start = 4
    else:
        self.header_start = 8

    # Get and decompress header
    header = f.read(header_size - self.header_start)
    self.header_data = zlib.decompress(header, -zlib.MAX_WBITS)

    # Get body
    self.body = f.read()
    f.close()

    # Get players data
    sep = b'\x04\x00\x00\x00Gaia'
    pos = self.header_data.find(sep) + len(sep)
    players = []
    for k in range(0, 8):
        id = struct.unpack('<I', self.header_data[pos:pos+4])[0]
        pos += 4
        type = struct.unpack('<I', self.header_data[pos:pos+4])[0]
        pos += 4
        name_size = struct.unpack('<I', self.header_data[pos:pos+4])[0]
        pos += 4
        name = self.header_data[pos:pos+name_size].decode('utf-8')
        pos += name_size
        if id < 9:
            players.append(Player(id, type, name))

希望它能帮助未来的程序员:)

顺便说一句,我正打算写一本这样的图书馆。在

相关问题 更多 >

    热门问题