Entwickler-Ecke
Dateizugriff - Compressed Data dekomprimieren (zLib)
Karlson - Mo 21.04.08 04:40
Titel: Compressed Data dekomprimieren (zLib)
Hallo,
Das ganze hat mit
diesem [
http://www.delphi-forum.de/topic_Wert+an+Offset+0xABCDE+einer+Datei_82731.html] Thread angefangen. Es geht darum bestimmte Daten aus einer Replay-Datei eines Computerspiels auszulesen.
Es gibt eine sehr gute Dokumentation zu diesen Replay-Dateien. Im oben genannten Thread ging es nun erstmal darum wie ich prinzipiell Daten deren Offset, Typ und Grösse mir bekannt ist aus einer Datei auslesen kann. Das wurde in dem Thread gelöst.
Jetzt bin ich aber an eine Stelle gekommen an der ich nicht mehr weiter weiss und hoffe dass jemand evt. in dem Bereich etwas Erfahrung hat und mir erklären kann wie ich da vorgehen muss.
Zunächsteinmal hier der Originaltext aus der Dokumentation:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61:
| =============================================================================== 3.0 [Data block header] ===============================================================================
Each compressed data block consists of a header followed by compressed data. The first data block starts at the address denoted in the replay file header. All following addresses are relative to the start of the data block header. The decompressed data blocks append to a single continueous data stream (disregarding the block headers). The content of this stream (see section 4) is completely independent of the original block boundaries.
offset | size/type | Description -------+-----------+----------------------------------------------------------- 0x0000 | 1 word | size n of compressed data block (excluding header) 0x0002 | 1 word | size of decompressed data block (currently 8k) 0x0004 | 1 dword | unknown (probably checksum) 0x0008 | n bytes | compressed data (decompress using zlib)
To decompress one block with zlib: 1. call 'inflate_init' 2. call 'inflate' with Z_SYNC_FLUSH for the block
The last block is padded with 0 bytes up to the 8K border. These bytes can be disregarded.
//TODO: add decompression details and move explanation to seperate file
=============================================================================== 4.0 [Decompressed data] ===============================================================================
Decompressed data is a collection of data items that appear back to back in the stream. The offsets for these items vary depending on the size of every single item.
This section describes the records that always appear at the beginning of a replay data stream. They hold information about settings and players right before the start of the game. Data about the game in progress is described in section 5.
The order of the start up items is as follows:
# | Size | Name ---+----------+-------------------------- 1 | 4 byte | Unknown (0x00000110 - another record id?) 2 | variable | PlayerRecord (see 4.1) 3 | variable | GameName (null terminated string) (see 4.2) 4 | 1 byte | Nullbyte 5 | variable | Encoded String (null terminated) (see 4.3) | | - GameSettings (see 4.4) | | - Map&CreatorName (see 4.5) 6 | 4 byte | PlayerCount (see 4.6) 7 | 4 byte | GameType (see 4.7) 8 | 4 byte | LanguageID (see 4.8) 9 | variable | PlayerList (see 4.9) 10 | variable | GameStartRecord (see 4.11)
The following sections describe these items in detail. After the static items (as described above) there follow variable information organized in blocks that are described in section 5. |
Der Subheader endet je nach Version an der Stelle 0x0040 oder 0x0042, also müsste dort dann der Data Block Header anfangen. Das Problem ist, dass die darauffolgenden Daten (4.0) irgendwie compressed sind. Der Autor der Dokumentation weisst darauf hin mal solle sie mit zlib dekomprimieren und könne sie dann erst auslesen...Wie soll dass den gehen? :lol: Kann mir da wirklich nichts drunter vorstellen gerade...
Hinzu kommt, dass der Autor jetzt von einem Stream redet.
Also mit zLib kann ich String komprimieren / dekomprimieren, das weiss ich bereits, aber mir fehlt ganz klar der Ansatz.
Sinspin - Mo 21.04.08 10:01
Stream oder String stellt hier keinen Unterschied dar.
Du nimmst einfach die Daten die du entschlüsseln willst und speicherst sie in einen String, dekomprimierst diesem mit zlib und arbeitest mit dem Ergebnis weiter. Dazu könntest du es ja auch wieder in eine datei speichern, so das du das gleiche vorgehen für die Zugriffe hast, das dir schon bekannt ist.
Karlson - Mo 21.04.08 15:37
Okay, was ich jetzt mal ausprobiert habe folgendes:
Ich weiss dass der Dataheader an 0x44 beginnt. An Stelle 0x44 steht dann auch die Grösse des Dataheaders.
Diese hab ich ausgelesen (3050, kann das sein?).
An Stelle 0x44+0x08 beginnt der Dataheader.
Dann hab ich mit readfile ausgelesen:
Delphi-Quelltext
1:
| readfile(hfile, integer($44+$8), StringDATA, DATASize) |
Diesen String müsste ich dann dekomprimieren, oder hab ich jetzt was falsch gemacht?
Ich hab nämlich schon mehrere String dekomprimier-codes ausprobiert und erhalte immer eine Zugriffsverletzung.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| function DeCompressString(input:string):string; var InpBuf, OutBuf: Pointer; OutBytes: Integer; begin InpBuf := nil; OutBuf := nil; try GetMem(InpBuf, Length(input)); Move(input[1], InpBuf^, Length(input)); DeCompressBuf(InpBuf, Length(input),0,OutBuf, OutBytes); SetLength(result,OutBytes); <--------------- AV Move(OutBuf^, result[1], OutBytes); finally if InpBuf <> nil then FreeMem(InpBuf); if OutBuf <> nil then FreeMem(OutBuf); end; end; |
Bei diesem Code aus der DP z.B.
Ich fürchte meine "Ausgangsdaten" sind falsch.
lg.
Karlson - Di 22.04.08 18:44
Hallo!
Ich werd hier nochmal pushen und die vorhandenen Fakten nochmal zusammentragen:
Die Dokumentation des Dateiformats sagt folgendes über den groben Aufbau:
Quelltext
1: 2:
| Header Endet bei: 0x30 bytes Subheader Endet bei: 0x44 bytes |
Darauf folgen die "Compressed Data Blocks".
Jeder "Compressed Data Block" besteht aus einem Header, gefolgt von den komprimierten Daten.
Der compressed Data Block (einschließlich Header) ist so aufgebaut:
Quelltext
1: 2: 3: 4: 5: 6:
| offset | size/type | Description -------+-----------+----------------------------------------------------------- 0x0000 | 1 word | size n of compressed data block (excluding header) 0x0002 | 1 word | size of decompressed data block (currently 8k) 0x0004 | 1 dword | unknown (probably checksum) 0x0008 | n bytes | compressed data (decompress using zlib) |
Wie die Dokumentation schon sagt, soll für das Dekomprimieren der compressed data zlib verwendet werden, darüberhinaus werden noch folgende zwei Hinweise gemacht:
Quelltext
1: 2: 3:
| To decompress one block with zlib: 1. call 'inflate_init' 2. call 'inflate' with Z_SYNC_FLUSH for the block |
Meine Frage, kurz und bündig: Wie komm ich an die Decompressed Data, über die später in der Dokumentation folgendes gesagt wird:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| Decompressed data is a collection of data items that appear back to back in the stream. The offsets for these items vary depending on the size of every single item.
This section describes the records that always appear at the beginning of a replay data stream. They hold information about settings and players right before the start of the game. Data about the game in progress is described in section 5.
The order of the start up items is as follows: |
Martok - Mi 23.04.08 12:01
Hm, den Puffer als String zu machen halte ich für potentiell Fehler-Erzeugend. Direkt mit Zeigern arbeiten ist meistens einfacher. Was du da hast, halte ich außerdem für problematisch insofern, dass alles 100mal hin und her gemoved wird ;)
Wenn du mir (ggf per PM) mal eine Solche Datei zukommen lässt (oder sagst welches Spiel, vllt kann ich ja selbst eine aufnehmen^^), dann würde ich mir das mal ansehen.
jaenicke - Mi 23.04.08 12:25
Im
Crosspost in der DP [
http://www.delphipraxis.net/post876531.html] hat er geschrieben, dass es sich um Warcraft III handelt ;-), den Crosspost hättest du ruhig auch andersherum kundtun können^^ (EDIT: das hat sich ja erledigt :D).
Ja, ich werd mal schauen, ob ich eine solche Datei von jemandem bekomme, dann kann ich es mal testen. Ohne ist es etwas schwierig, aber ich schau auch mal über deinen geposteten Quelltext.
Karlson - Mi 23.04.08 13:46
Servus!
Ich häng dir auch mal so eine Datei an: Die Erweiterung von zip in .w3g umbenennen.
lg.
jaenicke - Mi 23.04.08 15:11
0 Bytes? :lol:
Martok - Mi 23.04.08 17:07
So. Interessant, manchmal hilft Manual Lesen doch.
Also, abgesehen von deinem String-gebastel wäre, hätte das funktioniert, noch ein anderes Problem gekommen:
Die allgemeine zlib-Kapselung ruft die inflate-Funktion so auf:
Delphi-Quelltext
1:
| inflate(strm, Z_FINISH) |
Wie soll man das nochmal richtig machen?
Zitat: |
call 'inflate' with Z_SYNC_FLUSH for the block |
Ahja, also selber Kapselung schreiben. Schon gehts ;)
Im Anhang mal eine kleine Lösungsmöglichkeit. Bitte nicht mich für den Programmierstil schlagen, das hab ich nur mal schnell hingeschraubt. Bitte auch die Kommentare beachten.
Du solltest übrigens sattelfest in Pointerarithmetik sein, das was als nächstes kommt wird wildes Record-und-^Record-rumgewerfe ;)
Karlson - Fr 25.04.08 03:32
Hi Martok!
Vielen Dank für das Demoprojekt! Genial, dass du dir soviel Mühe damit gemacht hast. Das hilft mir wirklich weiter, obgleich ich jetzt erstmal versuchen muss, das was ich jetzt habe einzusetzen.
Ich habe ja besagten Crosspost in der DP eröffnet. Blöderweise wird jetzt mehr oder weniger parallel diskutiert. Das soll natürlich nicht sein, sorry dafür. Deswegen "mach ich hier mal zu".
Vielen Dank auch nochmal den anderen die geantwortet haben.
Lg.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!