2015-04-17 10 views
7

Sono un fan del obsoleto gioco Age of Empires II (AoE). Voglio scrivere un parser del record di gioco AoE (file .mgx) usando Python.parsing age of empires file di registrazione del gioco (.mgx)

Ho fatto qualche ricerca su GitHub e ho trovato piccoli progetti su questo, il più utile è aoc-mgx-format che fornisce some details of .mgx game record files.

Qui è il problema:

base al riferimento, struttura di un file .MGX è come:

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

L'ordine dei byte dei dati esadecimali in formato mgx è little endian.

header_len memorizza lunghezza dati della parte di intestazione (header_len + next_post + header_data)

header_data esercizi Imformation utile ho bisogno, ma la sua compresso con zlib

ho provato a decomprimere i dati in header_data con il modulo zlib come questo:

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 

ma ho ottenuto questo dopo l'esecuzione del programma:

Errore -3 mentre decompressione dei dati: controllo di intestazione errata

ps: provate .MGX file possono essere trovati qui: https://github.com/stefan-kolb/aoc-mgx-format/tree/master/parser/recs

+0

I dati non devono essere invertiti perché l'ordine dei byte è little-endian. Li hai già convertiti da little-endian in nativo usando '" abarnert

+2

C'è un errore nella tua domanda, dove dici "obsoleto gioco Age of Empires", penso che tu intenda "meravigliosamente fantastico gioco Age of Empires" . –

+0

Ad ogni modo, quando risolvi il problema (rimuovendo '[:: - 1]', questo corregge quell'errore e ti dà invece l'errore corretto -3, lamentando che EC BD non assomiglia ad un metodo di compressione valido. Dato che di solito vedrai 79 9C o 79 DA all'inizio di un blob compresso zlib valido, potrebbe valere la pena di scansionare il file per quei byte ... – abarnert

risposta

3

Il tuo primo problema è che voi non dovrebbe essere invertire i dati; basta sbarazzarsi del [::-1].

Ma se lo si fa, invece di ottenere quell'errore -3, si ottiene un errore diverso -3, in genere su un metodo di compressione sconosciuto.

Il problema è che questo è senza intestazione dati zlib, molto simile a quello che usa gzip. In teoria, ciò significa che le informazioni sul metodo di compressione, sulla finestra, su start dict, ecc. Devono essere fornite da qualche altra parte nel file (nel caso di gzip, per informazione nell'intestazione gzip). Ma in pratica, tutti usano deflate con la dimensione massima della finestra e non iniziano a dettare, quindi se stavo progettando un formato compatto per un gioco nei giorni in cui ogni byte contava, li avrei semplicemente codificati. (Nei tempi moderni, proprio questo è stato standardizzato in un RFC come "Formato DEFLATE dati compressi", ma la maggior parte 90s giochi per PC non sono stati in seguito RFC di progettazione ...)

Quindi:

>>> 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' 

Quindi, non solo decompresso, sembra una versione e ... beh, immagino che qualsiasi cosa assomigli a una costante sconosciuta, ma è la stessa costante sconosciuta nelle specifiche, quindi penso che siamo bravi.

quanto decompress documenti spiegano, MAX_WBITS è la dimensione della finestra/più comuni predefinito (e l'unico formato utilizzato da ciò che è comunemente chiamata "zlib sgonfiare" rispetto a "zlib"), e passando un valore negativo significa che l'header è soppresso; gli altri argomenti che possiamo lasciare ai valori predefiniti.

Vedere anche this answer, la sezione Advanced Functions nei documenti zlib e RFC 1951. (. Grazie al PO per trovare i collegamenti)

+1

thx molto, ho trovato questo con alcune parole chiave che hai fornito, anche utile [http://stackoverflow.com/a/22311297/4799491](http://stackoverflow.com/a/22311297/4799491) – lichifeng

+0

@ lichifeng: ho aggiunto i collegamenti alla risposta. Bella scoperta. – abarnert

2

Vecchio ma ecco un esempio di quello che ho fatto:

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)) 

Speranza che aiuta futuro programmatore :)

A WWAY Ho in programma scrivendo una tale biblioteca.

+0

Hai scritto quella libreria? – Bastiano

+0

@Bastiano è ancora in corso ma non ho molto più tempo in questi giorni (https://github.com/voblivion/AoE2RecordsParser). Sentiti libero di contribuire;) Controllerò qualsiasi richiesta/suggerimento di tiro. –